redis工具类封装

封装Redis工具类

  • 方法1:将任意Java对象序列化为json并存储在string类型的key中,并且可以设置TTL过期时间

  • 方法2:将任意Java对象序列化为json并存储在string类型的key中,并且可以设置逻辑过期时间,用于处理缓存击穿问题

  • 方法3:根据指定的key查询缓存,并反序列化为指定类型,利用缓存空值的方式解决缓存穿透问题

  • 方法4:根据指定的key查询缓存,并反序列化为指定类型,需要利用逻辑过期解决缓存击穿问题

    @Component
    @Slf4j
     public class CacheClient {
    
        private StringRedisTemplate stringRedisTemplate;
    
        private static final ExecutorService CACHE_REBUILD_EXECUTOR= Executors.newFixedThreadPool(10);
    
    
        public CacheClient(StringRedisTemplate stringRedisTemplate) {
            this.stringRedisTemplate = stringRedisTemplate;
        }
    
        //方法1:将任意Java对象序列化为json并存储在string类型的key中,并且可以设置TTL过期时间
        public void set(String key, Object value,Long time, TimeUnit unit){
            String jsonStr = JSONUtil.toJsonStr(value);
            stringRedisTemplate.opsForValue().set(key,jsonStr,time,unit);
        }
    
        //方法2:将任意Java对象序列化为json并存储在string类型的key中,并且可以设置逻辑过期时间, 用于处理缓存击穿问题
        public void setWithLogicExpire(String key,Object value,Long time,TimeUnit unit){
            //设置逻辑过期
            RedisData redisData = new RedisData();
            redisData.setData(value);
            redisData.setExpireTime(LocalDateTime.now().plusSeconds(unit.toSeconds(time)));
            //写入Redis
            stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(redisData));
        }
    
        //方法3:根据指定的key查询缓存,并反序列化为指定类型,利用缓存空值的方式解决缓存穿透问题
        public <R,ID> R queryWithPassThrought(String keyPrefix, ID id, Class<R> type, Function<ID,R> dbFallback,Long time,TimeUnit timeUnit) {
            String key= keyPrefix +id;
            //1.从redis查询缓存数据
            String json = stringRedisTemplate.opsForValue().get(key);
            //2.判断缓存数据是否存在
            if (StringUtils.isNotBlank(json)){
                //3.存在,直接返回
                return JSONUtil.toBean(json, type);
            }
            //判断命中的是否为空值
            if (json!=null){
                //返回一个错误信息
                return null;
            }
            //4.不存在,根据id查询
            R r = dbFallback.apply(id);
            //5.不存在,返回错误
            if (r==null){
                //将空值写入Redis
                stringRedisTemplate.opsForValue().set(key,"",RedisConstants.CACHE_NULL_TTL,TimeUnit.MINUTES);
                //返回错误信息
                return null;
            }
            //6.写入redis
            set(key,r,time,timeUnit);
            return r;
        }
    
        //方法4:根据指定的key查询缓存,并反序列化为指定类型,需要利用逻辑过期解决缓存击穿问题
        public <R,ID> R queryWithLogicExpire(String keyPrefix,ID id,Class<R> type,Function<ID,R> dbFallback,Long time,TimeUnit timeUnit){
            String key= keyPrefix +id;
            //1.从redis查
            String json = stringRedisTemplate.opsForValue().get(key);
            //2.判断是否存在
            if (StringUtils.isBlank(json)){
                //不存在,直接返回
                return null;
            }
            //3.命中,把json反序列化为对象
            RedisData redisData = JSONUtil.toBean(json, RedisData.class);
            R r=JSONUtil.toBean((JSONObject) redisData.getData(),type);
            LocalDateTime expireTime = redisData.getExpireTime();
            //4.查看是否过期
            if (expireTime.isAfter(LocalDateTime.now())){
                //未过期,直接返回
                return r;
            }
            //5.已过期,缓存重建
            //6.获取互斥锁
            String lockKey=RedisConstants.LOCK_SHOP_KEY+id;
            boolean isLock = tryLock(lockKey);
            //判断是否获取锁成功
            if (isLock){
                CACHE_REBUILD_EXECUTOR.submit(()->{
                    try {
                        //6.1查询数据库
                        R newR = dbFallback.apply(id);
                        //6.2重建缓存
                        setWithLogicExpire(key,newR,time,timeUnit);
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    } finally {
                        //6.3释放锁
                        unlock(key);
                    }
                });
            }
            //7.获取锁失败,返回过期的信息
            return r;
        }
        //方法4:根据指定的key查询缓存,并反序列化为指定类型,需要利用互斥锁解决缓存击穿问题
        public <R, ID> R queryWithMutex(String keyPrefix,ID id,Class<R> type,Function<ID,R> dbFallback,Long time,TimeUnit timeUnit){
            String key=keyPrefix+id;
            //1.从redis查询
            String json = stringRedisTemplate.opsForValue().get(key);
            //2.判断是否存在
            if (StringUtils.isNotBlank(json)){
                //存在直接返回
                return JSONUtil.toBean(json,type);
            }
            //3.判断命中的是否为空值
            if (json!=null){
                //返回一个错误信息
                return null;
            }
            //4.未命中,尝试获取互斥锁
            String lockKey=RedisConstants.LOCK_SHOP_KEY+id;
            R r = null;
            try {
                boolean isLock = tryLock(lockKey);
                // 4.1获取失败,休眠后继续获取
                if (!isLock){
                    Thread.sleep(50);
                    //递归查询
                    return queryWithMutex(keyPrefix, id, type,dbFallback, time, timeUnit);
                }
                //4.2获取成功,查询数据库
                r = dbFallback.apply(id);
                //4.3不存在,返回错误
                if (r==null){
                    //将空值写入redis
                    stringRedisTemplate.opsForValue().set(key,"",time,timeUnit);
                    //返回错误信息
                    return null;
                }
                //4.4存在,写回Redis
                set(key,JSONUtil.toJsonStr(r),time,timeUnit);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            } finally {
                unlock(lockKey);
            }
            //5.返回数据
            return r;
    
        }
    
        private boolean tryLock(String lockKey) {
            Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "1", RedisConstants.LOCK_SHOP_TTL, TimeUnit.SECONDS);
            return BooleanUtil.isTrue(flag);
        }
    
        private void unlock(String lockKey){
            stringRedisTemplate.delete(lockKey);
        }
    
    }
    
posted @ 2024-01-14 14:03  天菜星  阅读(136)  评论(0)    收藏  举报