相關依賴: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>
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; } }
根據須要,編寫工具類,方便使用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; }
這裏使用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
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