封装Redis工具类

基于StringRedisTemplate封装一个缓存工具类,满足下列需求:

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

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

存击穿问题

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

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

将逻辑进行封装

CacheClient.java
@Slf4j
@Component
public class CacheClient {

    private final StringRedisTemplate stringRedisTemplate;

    public CacheClient(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

     /**
     * 设置指定key对应的value值,使用JSON格式化字符串转换value对象为字符串进行存储。
     * @param key 要设置的key值
     * @param value 要设置的value值
     * @param time 过期时间
     * @param unit 过期时间的单位
     */
    public void set(String key, Object value, Long time, TimeUnit unit) {
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value), time, unit);
    }


      /**
     * 设置具有逻辑过期时间的值到指定的 key 中。
     *
     * @param key 要设置的 key
     * @param value 要设置的值
     * @param time 过期时间(单位:秒)
     * @param unit 过期时间的单位
     */
    public void setWithLogicalExpire(String key, Object value, Long time, TimeUnit unit) {
        // 创建 RedisData 对象,用于存储数据和过期时间
        RedisData redisData = new RedisData();
        redisData.setData(value);
        // 设置过期时间为当前时间加上传入的秒数
        redisData.setExpireTime(LocalDateTime.now().plusSeconds(unit.toSeconds(time)));
        // 将 RedisData 对象转换成 JSON 字符串,并设置到指定的 key 中
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(redisData));
    }


        /**
     * 通过传递的参数进行查询操作,并使用PassThroughExecutor进行缓存查询和数据库查询的 fallback 处理。
     *
     * @param keyPrefix 键的前缀
     * @param id        对应的ID值
     * @param type      查询结果的目标类型
     * @param dbFallback 数据库查询的 fallback 函数
     * @param time       缓存有效时间
     * @param unit       时间单位
     * @return 查询结果
     */
    public <R, T> R queryWithPassThrough(
            String keyPrefix, T id, Class<R> type, Function<T, R> dbFallback, Long time, TimeUnit unit) {

        String key = keyPrefix + id;

        // 从Redis中查询缓存
        String json = stringRedisTemplate.opsForValue().get(key);
        if (StrUtil.isNotBlank(json)) {
            return JSONUtil.toBean(json, type);
        }
        if (json != null) {
            return null;
        }

        R r = dbFallback.apply(id);
        if (r == null) {
            stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY + id, "", RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES);
            return null;
        }
        this.set(key, r, time, unit);
        return r;
    }


    private static final ExecutorService CACHE_REBUILD_EXECUTOR = Executors.newFixedThreadPool(10);

        /**
     * 使用逻辑过期时间查询缓存
     * @param keyPrefix 键的前缀
     * @param id 对应的id
     * @param type 缓存对象的类型
     * @param dbFallback 数据库回退函数
     * @param time 过期时间
     * @param unit 时间单位
     * @return 查询结果
     */
    public <R, T> R queryWithLogicalExpire(
            String keyPrefix, T id, Class<R> type, Function<T, R> dbFallback, Long time, TimeUnit unit) {
        // 从Redis查询缓存
        String key = keyPrefix + id;
        String json = stringRedisTemplate.opsForValue().get(key);
        if (StrUtil.isBlank(json)) {
            return null;
        }
        // 判断过期时间
        RedisData redisData = JSONUtil.toBean(json, RedisData.class);
        R r = JSONUtil.toBean((JSONObject) redisData.getData(), type);
        LocalDateTime expireTime = redisData.getExpireTime();
        if (expireTime.isAfter(LocalDateTime.now())) {
            return r;
        }
        String locKey = LOCK_SHOP_KEY + id;
        boolean isLock = tryLock(locKey);
        if (isLock) {
            // 开启线程重建缓存
            CACHE_REBUILD_EXECUTOR.submit(() -> {
                try {
                    R r1 = dbFallback.apply(id);
                    this.setWithLogicalExpire(key, r1, time, unit);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                } finally {
                    unLock(locKey);
                }
            });
        }
        return r;
    }


        /**
     * 尝试获取锁
     * @param key 锁的键值
     * @return 如果成功获取锁返回true,否则返回false
     */
    private boolean tryLock(String key) {
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
        return BooleanUtil.isTrue(flag);
    }


        /**
     * 删除指定key对应的value值
     *
     * @param key 要删除的key值
     */
    private void unLock(String key) {
        Boolean flag = stringRedisTemplate.delete(key);
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

悬溺-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值