spring boot 2.x ; redis 5.0.x;protobuf 3.x;
pom
配置
->redis配置
->fastJsonTemplateConfig
配置工具类
Jedis和Lettuce对比
reids
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
使用FastJson 来进行序列化(可选)
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.49version>
dependency>
使用 protobuf 来进行序列化(选用)
<dependency>
<groupId>com.baidugroupId>
<artifactId>jprotobufartifactId>
<version>2.2.9version>
dependency>
<dependency>
<groupId>com.google.protobufgroupId>
<artifactId>protobuf-javaartifactId>
<version>3.6.0version>
dependency>
注意 : 我们可选中不同的序列化机制来使用,本示例有使用 fastjson
和 protobuf
两种来进行实现。本文选用的 protobuf
的插件的github来源
redis 配置与lettuce配置(可换为 jedis两者区别见下文)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=192.168.137.128
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=123456
# 连接超时时间(毫秒)
spring.redis.timeout=5000
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.lettuce.pool.max-active= 600
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.lettuce.pool.max-wait= -1
#从池中取出连接前进行检验的校验时长
spring.redis.lettuce.pool.time-between-eviction-runs= 2000
# 连接池中的最大空闲连接
spring.redis.lettuce.pool.max-idle= 200
# 连接池中的最小空闲连接
spring.redis.lettuce.pool.min-idle=0
FastJson template 配置
使用FastJson 来进行序列化,实现Reids的序列化接口
public class FastJsonRedisSerializer<T> implements RedisSerializer<T> {
public static final Charset DEFAULT_UTF8 = Charset.forName("UTF-8");
private Class<T> tClass;
public FastJsonRedisSerializer(Class<T> tClass) {
super();
this.tClass = tClass;
}
@Override
public byte[] serialize(T t) throws SerializationException {
if (ObjectUtils.isEmpty(t)){
return new byte[0];
}
return JSONObject.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_UTF8);
}
@Override
public T deserialize(byte[] bytes) throws SerializationException {
if (ObjectUtils.isEmpty(bytes) || bytes.length == 0){
return null;
}
String str = new String(bytes, DEFAULT_UTF8);
return JSONObject.parseObject(str,tClass);
}
}
自定义redis模板继承 redisTemplate
,
@Component
// 先去配置文件读取 redis 相关的配置不然会导致 RedisConnectionFactory (LettuceConnectionFactory) 链接为空的
@AutoConfigureAfter(RedisAutoConfiguration.class)
@Import({RedisAutoConfiguration.class})
@Slf4j
public class FastJsonRedisTemplate extends RedisTemplate<String, Object> {
public FastJsonRedisTemplate(
@Value("#{'${IP.white.list}'.split(',')}") List<String> ipWhiteList,
@Autowired() LettuceConnectionFactory lettuceConnectionFactory) {
FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer(Object.class);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// 指定 fastJson 白名单
ipWhiteList.stream().forEach(ipWhite -> ParserConfig.getGlobalInstance().addAccept(ipWhite));
//指定包解析
ParserConfig.getGlobalInstance().addAccept("com.study.www");
setConnectionFactory(lettuceConnectionFactory);
afterPropertiesSet();
// key 直接使用 StringRedisSerializer 其无需复杂格式
setKeySerializer(stringRedisSerializer);
setHashKeySerializer(stringRedisSerializer);
setValueSerializer(fastJsonRedisSerializer);
setHashValueSerializer(fastJsonRedisSerializer);
logger.warn("the Lettuce-fastjson starting success,date is -->"+ new Date());
}
}
Protobuf template 配置,(可选,与 FastJson 的序列化方案选一个即可)
使用protobuf
来进行序列化,实现Reids的序列化接口(RedisSerializer)
public class ProtobufRedisSerializer<T > implements RedisSerializer<T> {
public static volatile Map<String, Codec> simpleTypeCodeMap = new HashMap<>();
public static final Charset UTF8 = Charset.forName("UTF-8");
private Class<T> tClass;
public ProtobufRedisSerializer(Class<T> tClass) {
super();
this.tClass = tClass;
}
public ProtobufRedisSerializer(T t) {
super();
this.tClass = (Class<T>) t.getClass();
}
@Override
public byte[] serialize(T t) throws SerializationException {
Codec<T> codec = getCodec(t.getClass());
try {
return codec.encode(t);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public T deserialize(byte[] bytes) throws SerializationException {
if (ObjectUtils.isEmpty(bytes) || bytes.length == 0){
return null;
}
try {
Codec<T> codec = getCodec(tClass);
return codec.decode(bytes);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private Codec<T> getCodec(Class clazz){
Codec codec = simpleTypeCodeMap.get(clazz.getTypeName());
if (ObjectUtils.isEmpty(codec)){
synchronized (ProtobufRedisSerializer.class) {
codec = Optional.ofNullable(codec).orElseGet(() -> ProtobufProxy.create(clazz));
simpleTypeCodeMap.put(tClass.getTypeName(),codec);
}
}
return codec;
}
}
自定义redis模板继承 redisTemplate
,
@Component
@AutoConfigureAfter(RedisAutoConfiguration.class)
@Import({RedisAutoConfiguration.class})
@Slf4j
public class ProtobufRedisTemplate extends RedisTemplate<String, Object> {
public ProtobufRedisTemplate( @Autowired() LettuceConnectionFactory lettuceConnectionFactory) {
ProtobufRedisSerializer protobufRedisSerializer = new ProtobufRedisSerializer(Object.class);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
setConnectionFactory(lettuceConnectionFactory);
afterPropertiesSet();
setKeySerializer(stringRedisSerializer);
setHashKeySerializer(stringRedisSerializer);
setValueSerializer(protobufRedisSerializer);
setHashValueSerializer(protobufRedisSerializer);
logger.warn("the Lettuce-protobuf starting success,date is -->"+ new Date());
}
}
```java
@Component
public class RedisUtils {
@Autowired
ProtobufRedisTemplate protobufRedisTemplate;
ValueOperations operations = null;
ListOperations operationsList = null;
/*** 默认生存周日 72小时 = 259200S */
private static final long TWODAY_TIME = 259200L;
@PostConstruct
public void init() {
operations = protobufRedisTemplate.opsForValue();
operationsList = protobufRedisTemplate.opsForList();
}
/**
* 从缓存中得到数据
*
* @param key key
* @return Object
*/
public Object get(String key) {
if (ObjectUtils.isEmpty(key)) {
return null;
}
return operations.get(key);
}
/**
* 设置数据到缓存中
*
* @param key key
* @param value value
*/
public void set(String key, Object value) {
if (!(ObjectUtils.isEmpty(key) || ObjectUtils.isEmpty(value))) {
operations.set(key, value, TWODAY_TIME, TimeUnit.SECONDS);
}
}
/**
* 设置数据到缓存中
*
* @param key key
* @param value value
* @param offset 过期时间 S
*/
public void set(String key, Object value, Long offset) {
if (!(ObjectUtils.isEmpty(key) || ObjectUtils.isEmpty(value))) {
offset = Optional.ofNullable(offset).orElseGet(() -> TWODAY_TIME);
operations.set(key, value, offset, TimeUnit.SECONDS);
}
}
/**
* 从缓存中删除数据
*
* @param key key
*/
public void delete(String key) {
if (!ObjectUtils.isEmpty(key)) {
protobufRedisTemplate.delete(key);
}
}
/**
* 判断key是否存在
*
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
if (!ObjectUtils.isEmpty(key)) {
return protobufRedisTemplate.hasKey(key);
}
return false;
}
/**
* 根据key 获取剩余过期时间
*
* @param key 键
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key) {
if (ObjectUtils.isEmpty(key)) {
throw new RuntimeException("the redis key is not null!");
}
return protobufRedisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 获取list缓存的内容
*
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
* @return List list中的所有元素
*/
public List
Jedis: 阻塞I/O,方法同步调用。其为线性执行,线程不安全。故需要通过连接池来使用
Lettuce: 基于 Netty 构建的事件驱动模型,方法异步调用。其线程安全,综上故一个Lettuce可完成多个操作。
csdn
gitee