SpringBoot Maven Protobuf Redis

相關依賴:java

<springboot.version>2.0.2.RELEASE</springboot.version>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.31</version>
        </dependency>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>3.6.1</version>
        </dependency>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java-util</artifactId>
            <version>3.6.1</version>
        </dependency>
        
           <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.5.1</version>
                <configuration>
                    <protocArtifact>com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier}</protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
                    <!-- proto文件目錄 -->
                    <protoSourceRoot>${project.basedir}/src/main/java/com/harrison/proto</protoSourceRoot>
                    <!-- 生成的Java文件目錄 -->
                    <!--<outputDirectory>${project.build.directory}/generated-sources/protobuf</outputDirectory>-->
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>test-compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

集成Protobuf


  • 配置<plugin>建議不要指定<outputDirectory>,用默認的就能夠。一不留神把代碼覆蓋掉,就恭喜了。
  • 指定目錄建立proto文件,User.proto
syntax = "proto3";//指定版本
option java_package = "com.harrison.protobuf";//制定生成java類包路徑
option java_outer_classname = "UsersModel";//制定生成java類名

message Users {

     repeated User users = 1;// proto沒有list類型,對應repeated
     message User{
          string id = 1;
          string name = 2;
          string sex = 3;
     }
}
  • proto類型有一坑,int32 i = 0 和 bool b = false 時,轉換成Json或者JavaBean時,爲null。由於protobuf3沒有required了int默認爲0,bool默認爲false,轉換時取空。因此基本類型一概用string
  • maven import後,運行Plugins/protobuf/protobuf:compile,能夠看到生成的UsersModel

ProtoBufUtil


根據須要,編寫工具類,方便使用git

private static final Logger logger = LoggerFactory.getLogger(ProtoBufUtil.class);

    private static final  JsonFormat.Printer printer = JsonFormat.printer();

    private static final JsonFormat.Parser parser = JsonFormat.parser();

    /**
     * Proto 轉化爲Json
     * @param target
     * @return
     */
    public static String copyProtoBeanToJson(GeneratedMessageV3 target){
        try {
            return printer.print(target);
        } catch (InvalidProtocolBufferException e) {
            logger.error("ProtoBufUtil複製到Json異常",e);
            return null;
        }
    }

    /**
     * javabean轉化爲Proto
     * @param <T>
     * @param source
     * @param target
     * @return
     */
    public static <T extends GeneratedMessageV3> T copyJavaBeanToProtoBean(Object source, T.Builder target) {
        // javaBean 轉換爲Json
        String sourceStr = JSONUtil.bean2json(source);
        try {
            parser.merge(sourceStr, target);
            return (T) target.build();
        } catch (InvalidProtocolBufferException e) {
            logger.error("ProtoBufUtil複製到Proto異常",e);
        }
        return null;
    }


    /**
     * proto 轉化爲javabean
     * @param source
     * @param target
     * @param <T>
     * @return
     */
    public static <T> T copyProtoBeanToJavaBean(GeneratedMessageV3 source, Class<T> target){
        // protoBuf 轉換爲Json
        String soutceStr = copyProtoBeanToJson(source);
        return (T) JSONUtil.json2Object(soutceStr,target);
    }

    /**
     * 使用proto序列化javabean
     * @param source
     * @param target
     * @return
     */
    public static byte[] serializFromJavaBean(Object source,GeneratedMessageV3.Builder target){
        GeneratedMessageV3 messageV3 = copyJavaBeanToProtoBean(source,target);
        if (null != messageV3){
            return messageV3.toByteArray();
        }
        return new byte[0];
    }

    /**
     * 使用proto反序列化javabean
     * @param source
     * @param parser
     * @param target
     * @param <T>
     * @return
     */
    public static <T> T deserializToJavaBean(byte[] source,Parser parser, Class<T> target) {
        try {
            return copyProtoBeanToJavaBean((GeneratedMessageV3) parser.parseFrom(source),target);
        } catch (InvalidProtocolBufferException e) {
            logger.error("發序列化錯誤",e);
        }
        return null;
    }

集成Redis

這裏使用springboot2的RedisTemplate,首先配置Serializer方式github

@Bean
    RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory redisConnectionFactory) {

        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        //不使用默認的序列化
        template.setEnableDefaultSerializer(false);
        //使用StringRedisSerializer來序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }

這裏有幾個坑須要注意redis

  • 首先想到的應該是自定義序列化方式ProtocbufRedisSerializer
public class ProtocbufRedisSerializer<T> implements RedisSerializer<T> {

    private Class<T> type;

    public ProtocbufRedisSerializer(Class<T> type) {
        this.type = type;
    }

    @Override
    public byte[] serialize(T t) throws SerializationException {
        if (t == null) {
            return new byte[0];
        }
        try {
            GeneratedMessageV3 gm = (GeneratedMessageV3) t;
            return gm.toByteArray();
        } catch (Exception ex) {
            throw new SerializationException("Cannot serialize", ex);
        }

    }

    @Override
    public T deserialize(byte[] bytes) throws SerializationException {
        if (bytes.length == 0) {
            return null;
        }
        try {
            Method method = type.getMethod("parseFrom", new Class[]{bytes.getClass()});
            return (T) method.invoke(type, new Object[]{bytes});
        } catch (Exception ex) {
            throw new SerializationException("Cannot deserialize", ex);
        }
    }

    public Class<T> getType() {
        return type;
    }

    public void setType(Class<T> type) {
        this.type = type;
    }
}

編碼確實沒問題,但解碼就醉了。
Protobuf由byte[]解碼到Bean須要指定type,這樣的話RedisTemplate單例就沒有辦法是用了。每一個ProtobufBean都寫一個解碼太冗餘,不接受。
網上查了一圈,spring-data-redis 使用 protobuf進行序列化和反序列被這個博主點醒了。
既然有ProtoBufUtil工具類,每次直接push(byte[])而後再byte[]=pop(),對應序列化反序列化完事。
要注意的是 template.setEnableDefaultSerializer(false);,同時不要設置emplate.setValueSerializer(serializer);spring


再後面就是建立RedisUtil,開始使用嘍。這裏分享一個RedisUtiljson

通過ProtoBuf編碼後放入redis,能夠減小空間1~2倍,仍是比較不錯的。springboot

相關文章
相關標籤/搜索