springboot+Lettuce+Protobuf+redis

springboot 整合 redis 选用

spring boot 2.x ; redis 5.0.x;protobuf 3.x;

目录

pom

配置

->redis配置

->fastJsonTemplateConfig

配置工具类

Jedis和Lettuce对比


pom

  1. reids

    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-data-redisartifactId>
    dependency>
    
  2. 使用FastJson 来进行序列化(可选)

    <dependency>
        <groupId>com.alibabagroupId>
        <artifactId>fastjsonartifactId>
        <version>1.2.49version>
    dependency>
    
  3. 使用 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>
    

    注意 : 我们可选中不同的序列化机制来使用,本示例有使用 fastjsonprotobuf 两种来进行实现。本文选用的 protobuf 的插件的github来源

配置

  1. 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 配置

  1. 使用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);
        }
    }
    
    
  2. 自定义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 的序列化方案选一个即可)

  1. 使用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;
        }
    }
    
  2. 自定义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 lGet(String key, Long start, Long end) {
        if (ObjectUtils.isEmpty(key)) {
            throw new RuntimeException("the redis key is not null!");
        }
        start = Optional.ofNullable(start).orElseGet(() -> 0L);
        end = Optional.ofNullable(end).orElseGet(() -> -1L);
        return operationsList.range(key, start, end);
    }

    /**
    * 设置list 值
    *
    * @param key   键
    * @param value 值
    */
    public void lPush(String key, Object value) {
        operationsList.leftPush(key, value);
    }

    /**
    * 获取list缓存的长度
    * @param key 键
    */
    public long lSize(String key){
        if (ObjectUtils.isEmpty(key)) {
            throw new RuntimeException("the redis key is not null!");
        }
        return operationsList.size(key);
    }
}
```
 

Jedis 和 Lettuce

Jedis: 阻塞I/O,方法同步调用。其为线性执行,线程不安全。故需要通过连接池来使用

Lettuce: 基于 Netty 构建的事件驱动模型,方法异步调用。其线程安全,综上故一个Lettuce可完成多个操作。

代码示例

csdn
gitee

你可能感兴趣的:(springBoot)