前置需知:
1.本文章和网上大部分博客配置不太一样,各位看官要分析一下自己的需求。集成protobuf 本文章主要是手动调用protobuf的序列化方法,而不是交由springboot 去做,会偏向原生java 使用方式
2.由于为了和公司其他的项目达成一致,所以版本,依赖 都尽量保证一致,所以版本需要各位看官具体决定了哈(团队使用时不同版本会有冲突)
另外:看了网上用了protostuff https://blog.csdn.net/shenTiBeiTaoKongLa/article/details/107123596可以试一下(因为我没试,可以反馈成功与否哦)。
3.考虑到其他项目使用原生的luttuce,不支持key/value 结构不一致,所以对redis key field value 都进行压缩了(关注官网变更哦,后面会支持。或者看一下sprinboot-redis的源码(sprinboot支持的),对原生的Luttuce集成一下)
4.由于初期设计的.proto文件,可能存在压缩不完全的问题(后面会具体聊),大家可以见仁见智了啊,欢迎反馈
话多说了,先上配置
pom依赖如下
<protobuf.version>3.24.0</protobuf.version>
<dependency><groupId>com.google.protobuf</groupId><artifactId>protobuf-java</artifactId><version>${protobuf.version}</version> <!-- protobuf --></dependency><dependency><groupId>com.google.protobuf</groupId><artifactId>protobuf-java-util</artifactId> <!--protobuf 工具类--><version>${protobuf.version}</version></dependency><!-- Spring Data Redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><version>${spring-boot.version}</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId><version>2.11.1</version> <!-- 请检查最新版本 --></dependency>
继承RedisTemplate 对象 生成个性化RedisTemplate bean,配置序列化反序列化,自定义序列化RedisSerializer<byte[]> byteRedisSerializer = new RedisSerializer() 重写内部类是最核心的地方!
@Component
@AutoConfigureAfter(RedisAutoConfiguration.class)
@Import({RedisAutoConfiguration.class})
@Slf4j
public class ProtobufRedisTemplate extends RedisTemplate<Object, Object> {public ProtobufRedisTemplate( @Autowired() LettuceConnectionFactory lettuceConnectionFactory) {ProtobufRedisSerializer protobufRedisSerializer = new ProtobufRedisSerializer(Object.class);StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();setConnectionFactory(lettuceConnectionFactory);afterPropertiesSet();**-- 重写RedisSerializer 序列反序列化方法,直接返回数据 ,核心!!!!**RedisSerializer<byte[]> byteRedisSerializer = new RedisSerializer() {@Overridepublic byte[] serialize(Object o) throws SerializationException {if (o instanceof byte[]){return (byte[])o;}return new byte[0];}@Overridepublic byte[] deserialize(byte[] bytes) {return bytes;}};setKeySerializer(byteRedisSerializer);setValueSerializer(byteRedisSerializer);setHashKeySerializer(byteRedisSerializer);setHashValueSerializer(byteRedisSerializer);logger.warn("the Lettuce-protobuf starting success,date is -->"+ new Date());}
}
redis 工具类
@Component
@Slf4j
public class RedisUtils {@Autowiredprivate ProtobufRedisTemplate protobufRedisTemplate;/*** HashSet 并设置时间* @param key 键* @param map 对应多个键值* @param time 时间(秒)* @return true成功 false失败*/public boolean p_hmset(byte[] key, Map<byte[], byte[]> map, int time) {try {protobufRedisTemplate.opsForHash().putAll(key, map);if (time > 0) {expire(key, time);}return true;} catch (Exception e) {e.printStackTrace();return false;}}/*** HashGetAll** @return 值*/public Map<byte[], byte[]> p_hgetall(byte[] key) {Map<Object, Object> map = protobufRedisTemplate.opsForHash().entries(key);Map<byte[], byte[]> resultMap = map.entrySet().stream().collect(Collectors.toMap(entry -> (byte[]) entry.getKey(), entry -> (byte[]) entry.getValue()));return resultMap;//return protobufRedisTemplate.opsForHash().entries(key);}
}
测试的代码
RedisData.RedisValue redisValue2 = RedisData.RedisValue.newBuilder().putValue("master_id", createRedisObject("xxxx")).putValue("item_code", createRedisObject("")).putValue("content_id", createRedisObject("xxxx")).build();final byte[] byteArray2 = redisValue2.toByteArray();//序列化Map<byte[], byte[]> test = new HashMap<>();final byte[] byteField1 = RedisData.RedisKey.newBuilder().setKey("xxxx").build().toByteArray();test.put(byteField1, byteArray);final byte[] byteField2 = RedisData.RedisKey.newBuilder().setKey("xxx").build().toByteArray();test.put(byteField2, byteArray2);byte[] bytes_key = RedisData.RedisKey.newBuilder().setKey("xxx").build().toByteArray();byte[] bytes_value = RedisData.RedisKey.newBuilder().setKey("xxx").build().toByteArray();redisUtils.p_hmset(bytes_value, test, 2592000);redisUtils.p_set(bytes_key, bytes_key, 2592000);
.proto文件数据结构如下(仅供参考):
这个是对可变的Map 进行压缩的,可能会压缩不完全
可以把map<string,RedisObject> value=1; 改成map<RedisKey,RedisObject> value=1;
syntax = "proto3";package xxxx;
import public "google/protobuf/timestamp.proto";
option optimize_for=CODE_SIZE;
option java_outer_classname = "RedisData";message RedisValue{map<string,RedisObject> value=1;
}message RedisKey{string key=1;
}message RedisObject{oneof value {string string_value = 1;int32 int_value = 2;double double_value = 3;google.protobuf.Timestamp Timestamp = 4;}
}
使用maven 将proto文件编译成java文件
<os.detected.classifier>windows-x86_64</os.detected.classifier><build><extensions><!--主要用于获取并设置与操作系统相关的属性--><extension><groupId>kr.motd.maven</groupId><artifactId>os-maven-plugin</artifactId><version>1.6.2</version></extension></extensions><plugins><!--protobuf plugins 插件--><plugin><groupId>org.xolstice.maven.plugins</groupId><artifactId>protobuf-maven-plugin</artifactId><version>0.6.1</version><configuration><protocArtifact>com.google.protobuf:protoc:3.24:exe:${os.detected.classifier}</protocArtifact><!--默认值,proto源文件路径--><protoSourceRoot>${project.basedir}/src/main/proto</protoSourceRoot></configuration><executions><execution><goals><goal>compile</goal><goal>test-compile</goal></goals></execution></executions></plugin></plugins></build>
使用方式:直接copy ,当做普通的java类使用就可以了,注意要和proto文件里面的package xxxx; 路径一致,否则会报错。
效果如下