SpringBootMavenProtobufRedis

23次阅读

共计 5117 个字符,预计需要花费 13 分钟才能阅读完成。

相关依赖:

        <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 = "UserModel";// 制定生成 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, 可以看到生成的 MtMsgModel

ProtoBufUtil


根据需要,编写工具类,方便使用

    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(MessageOrBuilder target){
        try {return printer.print(target);
        } catch (InvalidProtocolBufferException e) {logger.error("ProtoBufUtil 复制到 Json 异常",e);
            return null;
        }
    }

    /**
     * javabean 转化为 Proto
     * @param source
     * @param target
     * @param <T>
     * @return
     */
    public static <T extends Message> 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(MessageOrBuilder 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,Message.Builder target){return copyJavaBeanToProtoBean(source,target).toByteArray();}

    /**
     * 使用 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((MessageOrBuilder) parser.parseFrom(source),target);
        } catch (InvalidProtocolBufferException e) {logger.error("发序列化错误",e);
        }
        return null;
    }

集成 Redis

这里使用 springboot2 的 RedisTemplate,首先配置 Serializer 方式

    @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;
    }

这里有几个坑需要注意

  • 首先想到的应该是自定义序列化方式 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);


再后面就是创建 RedisUtil,开始使用喽。这里分享一个 RedisUtil

经过 ProtoBuf 编码后放入 redis,可以减少空间 1~2 倍,还是比较不错的。

正文完
 0