前置需知:
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() {
@Override
public byte[] serialize(Object o) throws SerializationException {
if (o instanceof byte[]){
return (byte[])o;
}
return new byte[0];
}
@Override
public 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 {
@Autowired
private 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; 路径一致,否则会报错。
效果如下