04.利用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 {@Autowiredprivate StringRedisTemplate redis;@Overridepublic 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执行之前@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//1.判断是否需要拦截ThreadLocalif (UserHolder.getUser()==null) {response.setStatus(401);return false;}//7.放行return true;}//渲染后返回给前台数据前@Overridepublic 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执行之前@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//1.获取请求头中的tokenString 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. RedisData接收数据

@Data
public class RedisData {private LocalDateTime expireTime;private Object data;
}

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

@RestController
@RequestMapping("/shop")
public class ShopController {@Resourcepublic 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 {@Resourceprivate StringRedisTemplate redis;private static final ExecutorService EXECUTOR_SERVICE = Executors.newFixedThreadPool(10);private boolean tryLock(String key) {Boolean setnx = redis.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.MINUTES);return BooleanUtil.isTrue(setnx);}private void unlock(String key) {redis.delete(key);}public Result queryShopById(Long id) {Shop shop = queryShopWithLogicExpire(id);if (shop == null) {return Result.fail("店铺不存在");}return Result.ok(shop);}//利用逻辑过期解决缓存击穿问题private Shop queryShopWithLogicExpire(Long id) {//1.从Redis查询商品缓存String cacheShop = redis.opsForValue().get("cache:shop:" + id);//2.未命中if (ObjectUtil.isEmpty(cacheShop)) {return null;}//3.命中RedisData redisDataWithShop = JSONUtil.toBean(cacheShop, RedisData.class);LocalDateTime expireTime = redisDataWithShop.getExpireTime();JSONObject shopData = (JSONObject) redisDataWithShop.getData();Shop shop = JSONUtil.toBean(shopData, Shop.class);//3.1判断缓存是否过期if (expireTime.isAfter(LocalDateTime.now())) {//3.2未过期,返回return shop;}//4.已过期,需要重建缓存//5.缓存重建//5.1获取互斥锁String lock = "lock:shop:" + id;boolean isLock = tryLock(lock);//5.2判断互斥锁是否成功if (isLock) {// TODO: 2023/8/9  //5.3成功,开启独立线程,实现缓存重建CACHE_REBUILD_EXECUTOR.submit(() -> {//缓存重构try {Shop shopItem = getById(id);RedisData redisData = new RedisData();redisData.setData(shopItem);redisData.setExpireTime(LocalDateTime.now().plusSeconds(180L));redis.opsForValue().set("cache:shop:" + id, JSONUtil.toJsonStr(redisData));} finally {unlock(lock);}});}//5.4返回过期商铺信息return shop;}
}

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

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

相关文章

⑤ Axios网络请求

Axios安装 cnpm install --save axios post请求需要用到的&#xff1a; cnpm install --save querystring(用来转换格式的) 引入 一般是全局引入&#xff0c;在main.js中引入 全局引入后的get和post方式使用 get请求方式 post请求方式 先引入&#xff1a; axios封装…

STM32 定时器自动重装载寄存器ARR带来的影响,ARPE0和1区别

ARR是啥 自动重载寄存器是预装载的。对自动重载寄存器执行写入或读取操作时会访问预装载寄存器。预装载寄存器的内容既可以直接传送到影子寄存器&#xff0c;也可以在每次发生更新事件 (UEV) 时传送到影子寄存器&#xff0c;这取决于 TIMx_CR1 寄存器中的自动重载预装载使能位 …

1.作用域

1.1局部作用域 局部作用域分为函数作用域和块作用域。 1.函数作用域: 在函数内部声明的变量只能在函数内部被访问&#xff0c;外部无法直接访问。 总结&#xff1a; (1)函数内部声明的变量&#xff0c;在函数外部无法被访问 (2)函数的参数也是函数内部的局部变量 (3)不同函数…

Redis——三个特殊的数据类型+事务

概述 全称为远程字典服务。 Redis——基础篇(包含redis在云服务上的docker化安装和连接以及常用命令)_连接docker中的redis_北岭山脚鼠鼠的博客-CSDN博客 Redis能干什么&#xff1f; 1.内存存储、持久化&#xff0c;内存中是断电即失&#xff0c;因此持久化很重要&#xff…

【LVS-NAT配置】

配置 node1&#xff1a;128&#xff08;客户端&#xff09; node2&#xff1a;135&#xff08;调度器&#xff09; RS&#xff1a; node3&#xff1a;130 node4&#xff1a;132 node2添加网络适配器&#xff08;仅主机模式&#xff09; [rootnode2 ~]# nmtui[rootnode2 ~]#…

ETLCloud+MaxCompute实现云数据仓库的高效实时同步

MaxCompute介绍 MaxCompute是适用于数据分析场景的企业级SaaS&#xff08;Software as a Service&#xff09;模式云数据仓库&#xff0c;以Serverless架构提供快速、全托管的在线数据仓库服务&#xff0c;消除了传统数据平台在资源扩展性和弹性方面的限制&#xff0c;最小化用…

欧拉公式之证明

首先&#xff0c;我们考虑复数函数的泰勒级数展开式。对于任意一个复数函数f(z)&#xff0c;我们可以将其在za处进行泰勒级数展开&#xff1a; f(z) f(a) f(a)(z-a) f(a)(z-a)^2/2! f(a)(z-a)^3/3! ... 其中f(a)表示f(z)在za处的导数&#xff0c;f(a)表示f(z)在…

[PaddlePaddle] [学习笔记] [上] 计算机视觉(卷积、卷积核、卷积计算、padding计算、BN、缩放、平移、Dropout)

1. 计算机视觉的发展历程 计算机视觉作为一门让机器学会如何去“看”的学科&#xff0c;具体的说&#xff0c;就是让机器去识别摄像机拍摄的图片或视频中的物体&#xff0c;检测出物体所在的位置&#xff0c;并对目标物体进行跟踪&#xff0c;从而理解并描述出图片或视频里的场…

TypeScript 语法

环境搭建 以javascript为基础构建的语言&#xff0c;一个js的超集&#xff0c;可以在任何支持js的平台中执行&#xff0c;ts扩展了js并且添加了类型&#xff0c;但是ts不能被js解析器直接执行&#xff0c;需要编译器编译为js文件&#xff0c;然后引入到 html 页面使用。 ts增…

04 mysql innodb record

前言 最近看到了 何登成 大佬的 "深入MySQL源码 -- Step By Step" 的 pdf 呵呵 似乎是找到了一些 方向 之前对于 mysql 方面的东西, 更多的仅仅是简单的使用[业务中的各种增删改查], 以及一些面试题的背诵 这里会参照 MySQL Internals Manual 来大致的看一下 i…

通过matlab对比music,mvdr以及tdoa三种定位算法的性能

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 MUSIC&#xff08;Multiple Signal Classification&#xff09; 4.2 MVDR&#xff08;Minimum Variance Distortionless Response&#xff09; 4.3 TDOA&#xff08;Time Difference o…

开发一个RISC-V上的操作系统(七)—— 硬件定时器(Hardware Timer)

目录 往期文章传送门 一、硬件定时器 硬件实现 软件实现 二、上板测试 往期文章传送门 开发一个RISC-V上的操作系统&#xff08;一&#xff09;—— 环境搭建_riscv开发环境_Patarw_Li的博客-CSDN博客 开发一个RISC-V上的操作系统&#xff08;二&#xff09;—— 系统引导…

AD域机器KMS自动激活

1、打开AD域控&#xff0c;点击DNS管理 2、创建其它记录 3、选择服务位置 SRV 4、输入相关信息 服务&#xff1a;_VLMCS协议&#xff1a;_TCP权重&#xff1a;100端口号&#xff1a;1688KMS服务器地址&#xff1a;10.3.0.211 5、成功&#xff0c;这时域内主机重启后&#xff0…

腾讯云CVM服务器2核2g1m带宽支持多少人访问?

腾讯云2核2g1m的服务器支持多少人同时访问&#xff1f;2核2g1m云服务器短板是在1M公网带宽上&#xff0c;腾讯云服务器网以网站应用为例&#xff0c;当大规模用户同时访问网站时&#xff0c;很大概率会卡在公网带宽上&#xff0c;所以压根就谈不上2核2G的CPU内存计算性能是否够…

烧写PYNQ镜像到SD卡

一&#xff0c;安装 Win32diskimager 首先将Micro SD卡插入读卡器的卡槽中&#xff0c;然后再将读卡器插入计算机USB接口&#xff0c;此时计算机将会识别到插入的可移动磁盘。双击打开Win32DiskImager-1.0.0.zip 压缩文件&#xff0c;里面win32diskimager-1.0.0-install.exe文…

Postman

Postman 简介下载安装 简介 Postman 是一款用于测试和开发 API&#xff08;应用程序编程接口&#xff09;的工具&#xff0c;它提供了用户友好的界面和丰富的功能&#xff0c;帮助开发者轻松地创建、测试、调试和文档化各种类型的 API。无论是在构建 Web 应用、移动应用还是其…

“深入解析Maven:安装、创建项目和依赖管理的完全指南“

目录 引言Maven的安装创建Maven项目之前的装备工作Eclipse创建新的Maven项目项目依赖管理 总结 引言 Maven是一个流行的项目管理工具&#xff0c;被广泛用于Java项目的构建、依赖管理和部署。它提供了一种简单而强大的方式来管理项目的各个方面&#xff0c;使开发人员能够更专…

LeetCode 1631. Path With Minimum Effort【最小瓶颈路;二分+BFS或DFS;计数排序+并查集;最小生成树】1947

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…

阿里云PolarDB数据库倚天ARM架构详细介绍

阿里云云原生数据库PolarDB MySQL版推出倚天ARM架构&#xff0c;倚天ARM架构规格相比X86架构规格最高降价45%&#xff0c;PolarDB针对自研倚天芯片&#xff0c;从芯片到数据库内核全链路优化&#xff0c;助力企业降本增效。基于阿里云自研的倚天服务器&#xff0c;同时在数据库…

pytest fixture 用于teardown工作

fixture通过scope参数控制setup级别&#xff0c;setup作为用例之前前的操作&#xff0c;用例执行完之后那肯定也有teardown操作。这里用到fixture的teardown操作并不是独立的函数&#xff0c;用yield关键字呼唤teardown操作。 举个例子&#xff1a; 输出&#xff1a; 说明&…