Redis框架(九):大众点评项目 缓存工具封装

news2024/11/25 4:39:58

大众点评项目 缓存工具封装

  • 需求:缓存工具封装
  • 业务实现
  • 代码总览
  • 总结

SpringCloud章节复习已经过去,新的章节Redis开始了,这个章节中将会回顾Redis实战项目 大众点评
主要依照以下几个原则

  1. 基础+实战的Demo和Coding上传到我的代码仓库
  2. 在原有基础上加入一些设计模式,stream+lamdba等新的糖
  3. 通过DeBug调试,进入组件源码去分析底层运行的规则和设计模式

代码会同步在我的gitee中去,觉得不错的同学记得一键三连求关注,感谢:
Redis优化-链接: RedisUnitToolProject
在这里插入图片描述

需求:缓存工具封装

我们已经在上面三节处理了各种细节:缓存穿透、缓存击穿、缓存雪崩

我们在开发过程中,如果每次缓存操作都处理一遍,那太繁琐了

为了开发的流畅和严谨,我们在这一节整合以往的四种处理方案,将其封装成工具类,这样就可以直接调工具,而不是RedisTemplate

在这里插入图片描述

业务实现

其实代码和上几节基本一直,但是为了封装处理, 需要回顾线程池、函数式编程接口 和泛型使用

@Slf4j
@Component
public class CacheClient {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    //需要回顾线程池、函数式编程接口 和泛型使用
    private static final ExecutorService CACHE_REBUILD_EXECUTOR = Executors.newFixedThreadPool(10);

定义好对应的属性


    public void set(String key, Object value, Long time, TimeUnit unit){
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value), time, unit);
    }

    public void setWithLogicalExpire(String key, Object value, Long time, TimeUnit unit){
        RedisData redisData = new RedisData();
        redisData.setExpireTime(LocalDateTime.now().plusSeconds(unit.toSeconds(time)));
        redisData.setObject(value);
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(redisData));
    }

定义好对应的两种存储方法,分别是通过为了 缓存穿透, 缓存击穿

缓存穿透


    //通过泛型来进行处理不可知的类型,通过函数式编程传入IT用户的查询信息
    public <R, ID> R queryWithPassThrough(String keyPrefix, ID id, Class<R> type,
                                          Function<ID, R> dbFallback, Long time, TimeUnit unit){
        log.debug("RedisTool method...");
        String key = keyPrefix + id;
        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(key, "", RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES);
            return null;
        }

        this.set(key, r, time, unit);

        return r;
    }

逻辑过期时间处理缓存击穿

    // 逻辑过期时间 通过线程池进行处理
    public <ID, R> R queryWithLogicalExpire(String keyPrefix, ID id, Class<R> type,
                                         Function<ID, R> dbFallback, Long time, TimeUnit unit){
        log.debug("RedisTool queryWithLogicalExpire Method...");
        String key = keyPrefix + id;
        String json = stringRedisTemplate.opsForValue().get(key);
        if(StrUtil.isBlank(json)){
            return null;
        }

        RedisData data = JSONUtil.toBean(json, RedisData.class);
//        Shop shop = JSONUtil.toBean((JSONObject) data.getObject(), Shop.class);
        JSONObject object = (JSONObject) data.getObject();
        R r = JSONUtil.toBean(object, type);

        if(data.getExpireTime().isAfter(LocalDateTime.now())){
            return r;
        }

        //过期了,就通过独立线程 -> 互斥锁处理缓存击穿
        String lockKey = RedisConstants.LOCK_SHOP_KEY + id;
        if (!tryLock(lockKey)) {
            log.debug("lockKey: " + lockKey + " Result: " + r);
            CACHE_REBUILD_EXECUTOR.submit(()->{
                try {
                    this.saveShop2Redis(id,  dbFallback, keyPrefix,  time,  unit);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                    /*e.printStackTrace();*/
                }finally {
                    //删除暂时的key, 释放互斥锁
                    stringRedisTemplate.delete(lockKey);
                }
            });
        }

        if(r==null){
            return null;
        }

        return r;
    }

    public boolean tryLock(String key){

        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", RedisConstants.LOCK_SHOP_TTL, TimeUnit.SECONDS);
        //这里不可以直接返回flag,因为在拆箱过程中可能出现flag为null的情况;
        return BooleanUtil.isTrue(flag);
    }

    public <ID, R> void saveShop2Redis(ID id,
                                       Function<ID, R> dbFallback,
                                       String keyPrefix,
                                       Long time,
                                       TimeUnit unit){

        R r = dbFallback.apply(id);
        setWithLogicalExpire(keyPrefix + id,  r, time, unit);

    }

}

这样我们就可以直接去调用了,

queryWithLogicalExpire

    @Override
    public Result queryById(Long id) {

        Shop shop = cacheClient.queryWithLogicalExpire(RedisConstants.CACHE_SHOP_KEY, id, Shop.class, this::getById,
                RedisConstants.CACHE_NULL_TTL, TimeUnit.HOURS);

        if(shop==null){
            return Result.fail("店铺不存在");
        }

        return Result.ok(shop);
    }

queryWithPassThrough

    public Result queryById(Long id) {
/*        Shop shop = cacheClient.queryWithPassThrough(key, id, Shop.class, idf -> getById(idf),
                RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES);*/

        Shop shop = cacheClient.queryWithPassThrough(RedisConstants.CACHE_SHOP_KEY, id, Shop.class, this::getById,
                RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES);

        if(shop==null){
            return Result.fail("店铺不存在");
        }

        return Result.ok(shop);
    }

代码总览

@Slf4j
@Component
public class CacheClient {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    //需要回顾线程池、函数式编程接口
    // 和泛型使用
    private static final ExecutorService CACHE_REBUILD_EXECUTOR = Executors.newFixedThreadPool(10);



    public void set(String key, Object value, Long time, TimeUnit unit){
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value), time, unit);
    }

    public void setWithLogicalExpire(String key, Object value, Long time, TimeUnit unit){
        RedisData redisData = new RedisData();
        redisData.setExpireTime(LocalDateTime.now().plusSeconds(unit.toSeconds(time)));
        redisData.setObject(value);
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(redisData));
    }


    //通过泛型来进行处理不可知的类型,通过函数式编程传入IT用户的查询信息
    public <R, ID> R queryWithPassThrough(String keyPrefix, ID id, Class<R> type,
                                          Function<ID, R> dbFallback, Long time, TimeUnit unit){
        log.debug("RedisTool method...");
        String key = keyPrefix + id;
        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(key, "", RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES);
            return null;
        }

        this.set(key, r, time, unit);

        return r;
    }

    // 逻辑过期时间 通过线程池进行处理
    public <ID, R> R queryWithLogicalExpire(String keyPrefix, ID id, Class<R> type,
                                         Function<ID, R> dbFallback, Long time, TimeUnit unit){
        log.debug("RedisTool queryWithLogicalExpire Method...");
        String key = keyPrefix + id;
        String json = stringRedisTemplate.opsForValue().get(key);
        if(StrUtil.isBlank(json)){
            return null;
        }

        RedisData data = JSONUtil.toBean(json, RedisData.class);
//        Shop shop = JSONUtil.toBean((JSONObject) data.getObject(), Shop.class);
        JSONObject object = (JSONObject) data.getObject();
        R r = JSONUtil.toBean(object, type);

        if(data.getExpireTime().isAfter(LocalDateTime.now())){
            return r;
        }

        //过期了,就通过独立线程 -> 互斥锁处理缓存击穿
        String lockKey = RedisConstants.LOCK_SHOP_KEY + id;
        if (!tryLock(lockKey)) {
            log.debug("lockKey: " + lockKey + " Result: " + r);
            CACHE_REBUILD_EXECUTOR.submit(()->{
                try {
                    this.saveShop2Redis(id,  dbFallback, keyPrefix,  time,  unit);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                    /*e.printStackTrace();*/
                }finally {
                    //删除暂时的key, 释放互斥锁
                    stringRedisTemplate.delete(lockKey);
                }
            });
        }

        if(r==null){
            return null;
        }

        return r;
    }

    public boolean tryLock(String key){

        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", RedisConstants.LOCK_SHOP_TTL, TimeUnit.SECONDS);
        //这里不可以直接返回flag,因为在拆箱过程中可能出现flag为null的情况;
        return BooleanUtil.isTrue(flag);
    }

    public <ID, R> void saveShop2Redis(ID id,
                                       Function<ID, R> dbFallback,
                                       String keyPrefix,
                                       Long time,
                                       TimeUnit unit){

        R r = dbFallback.apply(id);
        setWithLogicalExpire(keyPrefix + id,  r, time, unit);

    }

}

总结

在这里插入图片描述

测试一下

在这里插入图片描述
suc

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/86705.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

视频直播技术干货:一文读懂主流视频直播系统的推拉流架构、传输协议等

1、引言 随着移动网络网速的提升与资费的降低&#xff0c;视频直播作为一个新的娱乐方式已经被越来越多的用户逐渐接受。特别是最近这几年&#xff0c;视频直播已经不仅仅被运用在传统的秀场、游戏类板块&#xff0c;更是作为电商的一种新模式得到迅速成长。 本文将通过介绍实时…

爆火Chatgpt注册完全指南

1 chatgpt 简介 ChatGPT是一种语言模型&#xff0c;它被训练来对对话进行建模。它能够通过学习和理解人类语言来进行对话&#xff0c;并能够生成适当的响应。ChatGPT使用了一种叫做Transformer的神经网络架构&#xff0c;这是一种用于处理序列数据的模型&#xff0c;能够在输入…

第33篇 网络(三)FTP(一)

导语 上一节我们讲述了HTTP的编程&#xff0c;这一节讲述与其及其相似的FTP的编程。FTP即FileTransfer Protocol&#xff0c;也就是文件传输协议。FTP的主要作用&#xff0c;就是让用户连接上一个远程计算机&#xff0c;查看远程计算机有哪些文件&#xff0c;然后把文件从远程…

STM32F4 | STM32CubeMX 图形配置工具

文章目录一、STM32CubeMX 简介二、STM32CubeMX 运行环境搭建1.Java环境安装2.STM32CubeMX 图形化工具安装三、使用 STM32CubeMX 工具配置工程模板1.工程初步建立和保存2.RCC 设置3.时钟系统&#xff08;时钟树&#xff09;配置4.GPIO 功能引脚配置5.生成工程源码6.编写用户程序…

汽车行业:充分借力数据价值,推动数字化营销链路闭环

当下&#xff0c;汽车行业已慢慢由曾经的增量市场逐步转变为存量市场。更年轻的消费群体偏好、更精准智能的营销投放策略和强势入局的新能源汽车等因素都在推动着汽车行业的不断发展。对于汽车厂商和垂域媒体来说&#xff0c;进行丰富的人群洞察与用户分层&#xff0c;能挖掘更…

陪诊服务系统源码,可以在线预约陪诊师的软件平台

随着生活方式受互联网的影响&#xff0c;我们的各方面都变得更加便利&#xff0c;也伴随着我国人口老龄化&#xff0c;大部分年轻人都选择了外出打工&#xff0c;背井离乡&#xff0c;从而没有办法待在父母身边照顾&#xff0c;陪诊师就是在这个大背景下诞生了&#xff0c;陪诊…

班级校园网页设计作业 静态HTML我的班级网页 DW班级网站模板下载 大学生简单班级网页作品代码 我的大学网页制作 学生班级网页设计作业

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

LeetCode刷题复盘笔记—一文搞懂动态规划之121. 买卖股票的最佳时机问题(动态规划系列第二十篇)

今日主要总结一下动态规划的一道题目&#xff0c;121. 买卖股票的最佳时机 题目&#xff1a;121. 买卖股票的最佳时机 Leetcode题目地址 题目描述&#xff1a; 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一…

QML入门教程:QML和QtQuick简介以及QML实例

从 Qt 4.7 开始&#xff0c;Qt 引入了一种声明式脚本语言&#xff0c;称为 QML&#xff08;Qt Meta Language 或者 Qt Modeling Language&#xff09;&#xff0c;作为 C 语言的一种替代。而 Qt Quick 就是使用 QML 构建的一套类库。 QML 是一种基于 JavaScript 的声明式语言。…

激光焊接3系铝合金的工艺分析

随着汽车行业的不断壮大&#xff0c;纯电动汽车和燃料电池汽车将共同主导中国新能源汽车市场的未来发展。电动车轻量化和燃料电池的大量使用&#xff0c;必然涉及到越来越多的铝合金焊接工艺。铝合金具有良好的物理、化学和机械性能&#xff0c;是工业生产中一种重要的轻金属材…

搜索引擎搜索特定网站的方法 :site

前言 从13开始的贴吧时代&#xff0c;还记得当时在贴吧里要搜索某些特定的帖子或者关键字的时候&#xff0c;用的都是“吧内搜索”&#xff0c;但用过的人都知道&#xff0c;这个所谓的吧内搜索其实很难用&#xff0c;大家都是用关键字空格贴吧之类的办法去搜索相关的内容。我…

2025年350万辆市场空间!舱驾一体「抢」行泊/舱泊风头

过去三年是汽车智能化「细分作战」阶段&#xff0c;无论是智能驾驶还是智能座舱&#xff0c;由于品牌、车型定位不同&#xff0c;导致在不同价位区间、不同品牌、不同车型&#xff0c;舱内舱外智能化功能组合并不一致。 而随着跨域融合、中央计算平台带动汽车行业进入新的增长…

《树莓派项目实战》第九节 使用PCF8591模块和光敏电阻传感器测量光照强度

目录 8.1 PCF8691模块引脚介绍 8.2 工作原理 8.3 开启I2C接口 8.4 连接到树莓派 8.5 编写代码测量光照强度 在上一节我们使用光敏电阻传感器检测了环境中的光照是否达到了设定的阈值。在本节&#xff0c;我们将使用PCF8591模块和光敏电阻度传感器测量环境中光照强度的具体…

Continua CI捆绑的PostgreSQL数据库服务

Continua CI捆绑的PostgreSQL数据库服务 捆绑的PostgreSQL数据库服务已升级到15.1版。 现在可以使用表达式和变量在“配置存储库分支映射”对话框中为每个存储库指定默认分支。 在“停止”操作中添加了“将失败视为错误”选项。如果选中此项并且该操作位于“Try”块内&#xff…

Qt 模型视图编程之可编辑数据模型

背景 Qt 模型视图编程中模型定义了标准接口对数据进行访问&#xff0c;可根据需求继承对应的抽象模型类来实现自定义的数据模型。一个基本的数据模型至少要实现以下虚函数&#xff1a; ①&#xff0e;rowCount&#xff1a;行数&#xff0c;返回要显示多少行&#xff1b; ②&…

SpringBoot+Vue实现在线电子小说网站

文末获取源码 开发语言&#xff1a;Java 使用框架&#xff1a;spring boot 前端技术&#xff1a;JavaScript、Vue.js 、css3 开发工具&#xff1a;IDEA/MyEclipse/Eclipse、Visual Studio Code 数据库&#xff1a;MySQL 5.7/8.0 数据库管理工具&#xff1a;phpstudy/Navicat JD…

伙伴福利,100个项目彻底精通Java!【开源】

为了帮助更多的小伙伴&#xff0c;快速成长进步&#xff0c;冲进大厂中厂&#xff0c;我分享了很多的项目哟&#xff0c;例如&#xff1a; java项目精品实战案例 | JavaSwing实战项目 但很多小伙伴&#xff0c;还觉得不够&#xff0c;好吧&#xff01;今天就拿出压箱底的项目…

CPU工作原理概述

为了了解CPU的完整工作过程&#xff0c;我们需要知道两件事&#xff0c;第一&#xff0c;CPU是如何获取到数据或者指令的&#xff1b;第二&#xff0c;CPU是如何执行指令的。 目录 一、CPU和内存的交互方式 1、地址空间的概念 2、CPU的取值过程 二、CPU内部的执行过程 1、…

OceanBase-安装

文章目录部署规划机器准备统一配置hosts设置设置机器名和静态ip关闭大页配置信息查看时钟源ntp配置&#xff08;需要所有机器root&#xff09;磁盘规划创建用户配置limits.conf配置sysctl.conf关闭防火墙关闭SELinux克隆虚拟机差异化配置中控机设置无密码SSH登录目标机器时钟源…

【推送位置苹果群发iMessage推】如果Windows和Linux实现不同的传输层协议,那末因为数据格式的不同

推荐内容IMESSGAE相关 作者推荐内容iMessage苹果推软件 *** 点击即可查看作者要求内容信息作者推荐内容1.家庭推内容 *** 点击即可查看作者要求内容信息作者推荐内容2.相册推 *** 点击即可查看作者要求内容信息作者推荐内容3.日历推 *** 点击即可查看作者要求内容信息作者推荐…