封装的redis客户端
本文最后更新于660 天前,其中的信息可能已经过时,如有错误请发送邮件到2763981847@qq.com

这个封装工具类是一个 简易的Redis 客户端,它完成了以下功能:

  • 将任意对象缓存至 Redis。
  • 将任意对象缓存至 Redis(逻辑过期)。
  • 从 Redis 或数据库中获取数据(带缓存穿透解决)。
  • 从 Redis 或数据库中获取数据(带缓存击穿解决)。

目前功能还不够完善,只基本完成了redis的缓存部分的使用,预计后面会完成redis实现消息队列的相关封装。项目完整代码在https://github.com/2763981847/redis-client

此外,该项目已上传至maven中央仓库https://central.sonatype.com/artifact/cn.autumnclouds.redis/redis-client/0.0.1,大家可以直接在自己的项目中引用

<!-- redis-client -->
<dependency>
    <groupId>cn.autumnclouds.redis</groupId>
    <artifactId>redis-client</artifactId>
    <version>0.1.2</version>
</dependency>

以下是项目的主体redisClient类的实现,供大家参考:


public class RedisClient {
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    private static final ExecutorService CACHE_REBUILD_EXECUTOR
            = new ThreadPoolExecutor(10, 10, 0L, TimeUnit.MINUTES,
            new LinkedBlockingQueue<>(), runnable -> new Thread(runnable, "缓存重建线程"));


    /**
     * 将任意对象缓存至redis
     *
     * @param key        key
     * @param value      value
     * @param expireTime 过期时间
     * @param timeUnit   timeUnit
     */
    public void set(String key, Object value, long expireTime, TimeUnit timeUnit) {
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value), expireTime, timeUnit);
    }

    /**
     * 将任意对象缓存至redis(逻辑过期)
     *
     * @param key        key
     * @param value      value
     * @param expireTime 逻辑过期时间
     * @param timeUnit   timeUnit
     */
    public void setWithLogicExpire(String key, Object value, long expireTime, TimeUnit timeUnit) {
        LocalDateTime logicExpireTime = LocalDateTime.now().plusSeconds(timeUnit.toSeconds(expireTime));
        RedisData redisData = new RedisData(value, logicExpireTime);
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(redisData));
    }

    /**
     * 从redis或数据库中拿到数据(带缓存穿透解决)
     *
     * @param key        redis中的key
     * @param type       要查询的数据类型
     * @param expireTime 缓存过期时间
     * @param timeUnit   timeUnit
     * @param dbCallback 数据库回调操作(redis中未查到数据后进行的数据库查询操作)
     * @param         查询到的数据类型
     * @return 查询到的数据
     */
    public  T getWithPassThrough(String key, Class type, long expireTime, TimeUnit timeUnit, Supplier dbCallback) {
        //先尝试从redis中拿到数据
        String jsonString = stringRedisTemplate.opsForValue().get(key);
        //如果成功拿到
        if (jsonString != null) {
            //刷新过期时间
            stringRedisTemplate.expire(key, expireTime, timeUnit);
            //判断是否为空对象
            if ("".equals(jsonString)) {
                //是空对象则返回null
                return null;
            }
            //不为空则返回数据
            return JSONUtil.toBean(jsonString, type);
        }
        //如果redis中没有数据则查询数据库
        T data = dbCallback.get();
        if (data == null) {
            //未查到数据则缓存空对象
            stringRedisTemplate.opsForValue().set(key, "", RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES);
            //返回空
            return null;
        }
        //查到数据则缓存至redis并返回
        this.set(key, data, expireTime, timeUnit);
        return data;
    }

    /**
     * 从redis或数据库中拿到数据(带缓存击穿解决)
     *
     * @param key        redis中的key
     * @param type       要查询的数据类型
     * @param expireTime 缓存逻辑过期时间
     * @param timeUnit   timeUnit
     * @param dbCallback 数据库回调操作(redis中未查到数据后进行的数据库查询操作)
     * @param         查询到的数据类型
     * @return 查询到的数据
     */
    public  T getWithLogicExpire(String key, Class type, long expireTime, TimeUnit timeUnit, Supplier dbCallback) {
        //先尝试从redis中拿到数据
        String jsonString = stringRedisTemplate.opsForValue().get(key);
        //缓存未命中则返回空
        if (StrUtil.isBlank(jsonString)) {
            return null;
        }
        //缓存命中则判断缓存是否已过期
        RedisData redisData = JSONUtil.toBean(jsonString, RedisData.class);
        T data = Convert.convert(type, redisData.getData());
        if (LocalDateTime.now().isBefore(redisData.getExpireTime())) {
            //未过期则刷新缓存并返回
            this.setWithLogicExpire(key, data, expireTime, timeUnit);
            return data;
        }
        //已过期则尝试重建缓存
        tryReBuildData(key, expireTime, timeUnit, dbCallback);
        return data;
    }

    /**
     * 尝试重建缓存
     *
     * @param key        key
     * @param expireTime 缓存逻辑过期时间
     * @param timeUnit   timeUnit
     * @param dbCallback 数据库查询函数
     * @param         数据库返回数据类型
     */
    private  void tryReBuildData(String key, long expireTime, TimeUnit timeUnit, Supplier dbCallback) {
        //尝试获取互斥锁进行缓存重建
        boolean tryLock = tryLock(key);
        if (tryLock) {
            //获取到锁调用用其他线程开始进行缓存重建
            CACHE_REBUILD_EXECUTOR.submit(() -> {
                try {
                    //查询数据库
                    T t = dbCallback.get();
                    //写入redis
                    this.setWithLogicExpire(key, t, expireTime, timeUnit);
                } finally {
                    //释放锁
                    unlock(key);
                }
            });
        }
    }

    /**
     * 尝试获得redis锁
     *
     * @param key 锁的key
     * @return 是否获取成功
     */
    private boolean tryLock(String key) {
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(RedisConstants.LOCK_KEY + key, "lock", RedisConstants.LOCK_TTL, TimeUnit.SECONDS);
        return BooleanUtil.isTrue(flag);
    }


    /**
     * 解除redis锁
     *
     * @param key 锁的key
     */
    private void unlock(String key) {
        stringRedisTemplate.delete(key);
    }
}

文章:封装的redis客户端
作者:oreki
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0协议。转载请注明文章地址及作者

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇