03.利用Redis实现缓存功能---解决缓存穿透版

学习目标:

提示:学习如何利用Redis实现添加缓存功能解决缓存穿透版


学习产出:

缓存穿透讲解图
在这里插入图片描述
解决方案:

  1. 采用缓存空对象
  2. 采用布隆过滤器
    解决方案流程图
    在这里插入图片描述

1. 准备pom环境

		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3</version>
        </dependency>
        <!--hutool-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.17</version>
        </dependency>

2. 配置ThreadLocal和过滤器

public class UserHolder {
    private static final ThreadLocal<UserDTO> tl = new ThreadLocal<>();

    public static void saveUser(UserDTO user){
        tl.set(user);
    }

    public static UserDTO getUser(){
        return tl.get();
    }

    public static void removeUser(){
        tl.remove();
    }
}
@Configuration
public class MvcConfig implements WebMvcConfigurer {
    @Autowired
    private StringRedisTemplate redis;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor()).excludePathPatterns("/user/code","/user/login","/blog/hot","/shop/**","/shop-type/**","/voucher/**").order(2);
        registry.addInterceptor(new RefreshTokenInterceptor(redis)).addPathPatterns("/**").order(1);
    }
}
---------------------------------------------
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {

    //controller执行之前
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //1.判断是否需要拦截ThreadLocal
        if (UserHolder.getUser()==null) {
            response.setStatus(401);
            return false;
        }
        //7.放行
        return true;
    }
    //渲染后返回给前台数据前
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        //移除用户,避免内存泄露
        UserHolder.removeUser();
    }
}
---------------------------------------------------
@Slf4j
public class RefreshTokenInterceptor implements HandlerInterceptor {
    //这个对象不是由spring管理的所以不能用注解自动注入

    private StringRedisTemplate redis;

    public RefreshTokenInterceptor(StringRedisTemplate redis) {
        this.redis = redis;
    }

    //controller执行之前
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //1.获取请求头中的token
        String token = request.getHeader("authorization");
        if (StrUtil.isBlank(token)) {
            return true;
        }
        //2.基于token获取redis中的用户
        //通过key取到hash中的map集合数据
        Map<Object, Object> userMap = redis.opsForHash().entries("login:token:" + token);
        //3.判断用户是否存在
        if (userMap.isEmpty()) {
            return true;
        }
        //5.将查询到的hash数据转为userDto对象
        UserDTO userDTO = BeanUtil.fillBeanWithMap(userMap, new UserDTO(), false);
        //6.存在,保存用户信息到ThreadLocal中
        UserHolder.saveUser(userDTO);
        //7.刷新token有效期
        redis.expire(LOGIN_USER_KEY + token, 30, TimeUnit.MINUTES);
        log.info("我是第一个拦截器当前拦截所有请求的用户为,线程为{},{}",UserHolder.getUser(),Thread.currentThread());
        //8.放行
        return true;
    }

3. Controller层:负责接收请求和向下分配

@RestController
@RequestMapping("/shop")
public class ShopController {
    @Resource
    public IShopService shopService;
    /**
     * 根据id查询商铺信息
     * @param id 商铺id
     * @return 商铺详情数据
     */
    @GetMapping("/{id}")
    public Result queryShopById(@PathVariable("id") Long id) {
        return Result.ok(shopService.queryShopById(id));
    }
}

4. Service层:负责业务的处理逻辑

@Service
@Slf4j
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {
    @Resource
    private StringRedisTemplate redis;
    
    public Result queryShopById(Long id) {
        //1.从Redis查询数据缓存
        String shopCache = redis.opsForValue().get("cache:shop:" + id);
        //2.判断是否存在 当shopCache为“”时返回false
        if (StrUtil.isNotBlank(shopCache)) {
            //3.存在,直接返回
            Shop shop = JSONUtil.toBean(shopCache, Shop.class);
            return Result.ok(shop);
        }
        //判断命中的是否是空值
        if (shopCache!=null) {
            return Result.fail(" 店铺信息不存在 ");
        }
        //4.不存在,根据id查询数据库
        Shop shop = getById(id);
        if (ObjectUtil.isEmpty(shop)) {
            // 解决缓存穿透
            redis.opsForValue().set("cache:shop:" + id,"",2,TimeUnit.MINUTES);
            //5.不存在,返回错误
            return Result.fail("当前商户不存在");
        }
        //6.存在,写入redis
        redis.opsForValue().set("cache:shop:"+id,JSONUtil.toJsonStr(shop));
        redis.expire("cache:shop:"+id,30,TimeUnit.MINUTES);
        //7.返回
        return Result.ok(shop);
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Redis储存结构

Redis怎么储存的 这个redisDb是数据库对象 里面的其他字段忽略了 然后里面有个dict列表(字典列表) 我们随便来看一个redisObject 区分一下子啊 他这个dict里面没有存redisObject的对象 也没有存dict对象 它只是存了个数据指针 你看那个redis每个底层编码 抠搜的 这块要是再保存…

gin和gorm框架安装

理论上只要这两句命令 go get -u gorm.io/gorm go get -u github.com/gin-gonic/gin然而却出现了问题 貌似是代理问题&#xff0c;加上一条命令 go env -w GOPROXYhttps://goproxy.cn,direct 可以成功安装 安装gorm的数据库驱动程序 go get -u gorm.io/driver/mysql

完整版:TCP、UDP报文格式

目录 TCP报文格式 报文格式 报文示例 UDP报文格式 报文格式 报文示例 TCP报文格式 报文格式 图1 TCP首部格式 字段长度含义Source Port16比特源端口&#xff0c;标识哪个应用程序发送。Destination Port16比特目的端口&#xff0c;标识哪个应用程序接收。Sequence Numb…

【Linux 网络】NAT技术——缓解IPv4地址不足

NAT技术 NAT 技术背景NAT IP转换过程NAPTNAT 技术的缺陷 NAT&#xff08;Network Address Translation&#xff0c;网络地址转换&#xff09;技术&#xff0c;是解决IP地址不足的主要手段&#xff0c;并且能够有效地避免来自网络外部的攻击&#xff0c;隐藏并保护网络内部的计算…

Ansible从入门到精通【六】

大家好&#xff0c;我是早九晚十二&#xff0c;目前是做运维相关的工作。写博客是为了积累&#xff0c;希望大家一起进步&#xff01; 我的主页&#xff1a;早九晚十二 专栏名称&#xff1a;Ansible从入门到精通 立志成为ansible大佬 ansible templates 模板&#xff08;templa…

【JAVA】七大排序算法(图解)

稳定性&#xff1a; 待排序的序列中若存在值相同的元素&#xff0c;经过排序之后&#xff0c;相等元素的先后顺序不发生改变&#xff0c;称为排序的稳定性。 思维导图&#xff1a; &#xff08;排序名称后面蓝色字体为时间复杂度和稳定性&#xff09; 1.直接插入排序 核心思…

ASL国产CS5213 转VGA信号输出音频 替代AG6200安格芯片 HDMI to VGA(带音频)方案设计原理图

CS5213功能&#xff1a;HDMI转VGA带音频输出&#xff0c;专注于设计HDMI转VGA带音频输出。可替代AG6200 AG6201。 CS5213芯片是一个HDMI&#xff08;高清多媒体接口&#xff09;到VGA桥接芯片。 它将HDMI信号转换为标准VGA信号它可以在适配器、智能电缆等设备中设计。 Capst…

基于MATLAB小波变换的信号突变点检测

之前在不经意间也有接触过求突变点的问题。在我看来&#xff0c;与其说是求突变点&#xff0c;不如说是我们常常玩的"找不同"。给你两幅图像&#xff0c;让你找出两个图像中不同的地方&#xff0c;我认为这其实也是找突变点在生活中的应用之一吧。回到找突变点位置上…

virt-manager上安装ubuntu22.04虚拟机

文章目录 前言一、镜像下载二、 virt-manager新建机器2.1 选择安装来源类型2.2 选择ISO文件2.3 设置CPU数量和内存容量2.4 设置硬盘容量2.5 设置虚拟机类型&#xff0c;勾选配置按钮2.6 修改硬盘驱动类型2.7 修改网卡驱动类型2.8 设置显示器类型2.9 开始安装 三、操作系统安装3…

idea找不到DataBase

一、我想把数据库跟我的idea链接&#xff0c;结果发现找不到。如图。 二、解决方案 找到 file ---setting 找到plugin------找到marketplace 我的已经出现了

.NET根据类的值进行序列化反序列化操作

前言&#xff1a; 在.NET种&#xff0c;序列化一般常用的方式是使用Newtonsoft.Json进行序列化和反序列化操作&#xff0c;比如创建一个Person类 public class Person {public string Name { get; set; }public int Age { get; set; } }序列化为json // 对象序列化为 JSONPe…

Python-OpenCV中的图像处理-图像平滑

Python-OpenCV中的图像处理-图像平滑 图像平滑平均滤波高斯模糊中值模糊双边滤波 图像平滑 使用低通滤波器可以达到图像模糊的目的。这对与去除噪音很有帮助。其实就是去除图像中的高频成分&#xff08;比如&#xff1a;噪音&#xff0c;边界&#xff09;。所以边界也会被模糊…

stm32常见数据类型

stm32的数据类型的字节长度 s8 占用1个byte&#xff0c;数据范围 -2^7 到 (2^7-1) s16 占用2个byte&#xff0c;数据范围 -2^15 到 (2^15-1) s32 占用 4个byte&#xff0c;数据范围 -2^31 到 (231-1)231 2147483647 int64_t占用8个byte&#xff0c;数据范围 -2^63 到 (2^63-1)…

【cs61b】学习笔记day2

历史文章目录 【cs61b】学习笔记day1 文章目录 历史文章目录List两个小问题bits声明一个变量引用类型方框和指针表示法数组的实例化链表 SLList List 两个小问题 思考下面两个代码分别输出什么 Walrus a new Walrus(1000, 8.3); Walrus b; b a; b.weight 5; System.out.…

43.字符串相乘

目录 一、题目 二、代码 一、题目 43. 字符串相乘 - 力扣&#xff08;LeetCode&#xff09; 二、代码 class Solution { public: string addStrings(string num1, string num2)//求两个字符串相加 {int end1 num1.size() - 1;int end2 num2.size() - 1;int next 0;//进位…

windows 10 远程桌面配置

1. 修改远程桌面端口&#xff08;3389&#xff09; 打开注册表&#xff08;winr&#xff09;, 输入regedit 找到配置项【计算机\HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Terminal Server\Wds\rdpwd\Tds\tcp】 &#xff0c; 可以通过搜索“Wds”快速定位。 修改端口配…

PHPExcel单独设置(高亮)单元格部分文字字体颜色 导出文件(两种方法)

public function exportData(){ $objPHPExcel new \PHPExcel();//创建一个富文本对象$objRichText new \PHPExcel_RichText();$objRichText->createText(铁扇公主);//需要改变大小或颜色的文字内容$objPayable $objRichText->createTextRun(芭蕉妹妹);//设置加粗$objP…

Python-OpenCV中的图像处理-图像梯度

Python-OpenCV中的图像处理-图像梯度 图像梯度Sobel 算子和 Scharr 算子Laplacian 算子 图像梯度 图像梯度&#xff0c;图像边界等使用到的函数有&#xff1a; cv2.Sobel()&#xff0c; cv2.Scharr()&#xff0c; cv2.Laplacian() 等原理&#xff1a;梯度简单来说就是求导。Op…

[保研/考研机试] 括号匹配问题 C++实现

题目描述&#xff1a; 在某个字符串(长度不超过100)中有左括号、右括号和大小写字母&#xff1b;规定(与常见的算数式子一样)任何一个左括号都从内到外与在它右边且距离最近的右括号匹配。写一个程序&#xff0c;找到无法匹配的左括号和右括号&#xff0c;输出原来的字符串&am…

MachineLearningWu_14/P65-P69_Multiclass

x.1 Multiclass多分类问题 对于分类问题&#xff0c;往往指的是二分类问题&#xff0c;而对于二分类的decision boundary较为简单&#xff0c;而实际生活中会有很多问题是多分类问题&#xff0c;例如MNIST手写数字识别&#xff0c; 从特征空间上来看&#xff0c;二分类和多分类…