Redis的应用——接口幂等性,分布式锁,基于注解+拦截器的接口幂等改进

在这里插入图片描述

目录

  • 引出
  • 接口幂等&分布式锁
    • 接口幂等性
    • Redisson框架
    • 接口幂等改进
  • 缓存三兄弟:缓存击穿、穿透、雪崩
    • 缓存击穿
    • 缓存穿透
    • 缓存雪崩
  • 总结

引出

Redis的应用——接口幂等性,分布式锁,基于注解+拦截器的接口幂等改进


接口幂等&分布式锁

系统的问题:

接口的幂等性:接口幂等性就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生了副作用;

@RestController
@RequestMapping("/order")
public class OrderController {private final RedisTemplate redisTemplate;public OrderController(RedisTemplate redisTemplate) {this.redisTemplate = redisTemplate;}@PostMapping("/make")public String makeOrder(OrderDto orderDto){if(redisTemplate.hasKey("ORDER_" + orderDto.getOrderNum())){return "请求已经处理,请勿重复提交!";}//setnx key value 做到:判断key是否存在,如果key则不能加入到redis中去,称为:redis的分布式锁redisTemplate.opsForValue().setIfAbsent("ORDER_" + orderDto.getOrderNum(), "",10, TimeUnit.MINUTES);//调用业务层:完成订单的创建System.out.println("orderDto = " + orderDto);return "ok";}
}

接口幂等性

if(redisTemplate.hasKey("ORDER_" + orderDto.getOrderNum())){return "请求已经处理,请勿重复提交!";
}

redis的分布锁,缺陷:业务如果超出了上锁的时间,可能会导致数据不准确

Redisson框架

Redisson框架:它是Redis的封装框架,提供了Redis的所有操作

<!--引入redis的封装框架redission-->
<dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.10.0</version>
</dependency>

在这里插入图片描述

添加配置类:

@Configuration
public class RedissonConfig {@Autowiredprivate Environment env;//解决redission 和 Springcache的兼容问题@Beanpublic RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) // 设置缓存数据的key序列化方式为StringRedisSerializer.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())); // 设置缓存数据的value序列化方式为GenericJackson2JsonRedisSerializerRedisCacheManager cacheManager = RedisCacheManager.builder(connectionFactory).cacheDefaults(config).build();return cacheManager;}@Beanpublic RedissonClient redissonClient() {Config config = new Config();SingleServerConfig serverConfig = config.useSingleServer();// 设置 Redis 单节点地址和密码String address = "redis://" + env.getProperty("spring.redis.host") + ":" + env.getProperty("spring.redis.port");serverConfig.setAddress(address);serverConfig.setPassword(env.getProperty("spring.redis.password"));return Redisson.create(config);}
}

Redis上锁的方式:

//第1种
redisTemplate.opsForValue.setIfAbsent(key,value,time,TimeUnit)//第2种    
RLock lock = redissonClient.getLock(key);
lock.lock(time,TimeUnit)//上锁

不管是第1种,还是第2种,底层都是:setnx key value px time;上面的2种有可能有问题,问题在于:业务可能超过:上锁的时间,导致业务还没完成,锁就已经失效

解决方案:看门狗机制

RLock lock = redissonClient.getLock(key);
try {//获得一把锁,锁的时间默认是30S,间隔10S检测一次锁的状态,如果超过10S锁的状态,仍旧是在使用,就把锁的时间重置为30S//配置 看门狗lock = redissonClient.getLock(lock.getName());lock.lock();//上锁    //写:上锁的以后的业务代码
}catch (Exception e){
}finally {lock.unlock();//立即解锁
}

接口幂等改进

使用 注解 + 拦截器完成对于 接口 进行幂等处理

HTML页面

在这里插入图片描述

OrderController

在这里插入图片描述


添加接口幂等前缀常量:

public class BookConstant {public static final String BOOK_TYPE_LIST = "BOOKTYPELIST";public static final String TOKEN_PREFIX= "TOKEN_";public static final String USER_CART_PREFIX = "USER_CART_";//接口幂等public static final String INTERFACE_IDEA_PREFIX="INTERFACE_IDEM_";
}

自定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Idempotent {/*** 幂等过期时间,即:在此时间段内,对API进行幂等处理。*/long expireTime();/*** 时间单位* @return*/TimeUnit timeunit();
}

自定义拦截器

public class IdempotentInterceptor implements HandlerInterceptor {@Autowiredprivate RedisTemplate redisTemplate;/*** 控制层方法,执行之前进行拦截* @param request 请求对象* @param response 响应对象* @param handler 方法对象* @return* @throws Exception*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("handler = " + handler.getClass());//不是拦截方法,则放行if(!(handler instanceof HandlerMethod))return true;//判断方法对象是否添加 幂等注解HandlerMethod hm = (HandlerMethod) handler;boolean flag = hm.hasMethodAnnotation(Idempotent.class);if(!flag) return true;//如果没有使用幂等注解,直接放行//如果使用幂等注解,启动幂等配置Idempotent idem = hm.getMethodAnnotation(Idempotent.class);//identification = 前端:请求的唯一标识String identification = request.getHeader("Identification");System.out.println("Identification = " + identification);System.out.println("idem.expireTime() = " + idem.expireTime());System.out.println("idem.timeunit() = " + idem.timeunit());Boolean b = redisTemplate.hasKey(identification);if(b){extracted(response);return false;}else{redisTemplate.opsForValue().setIfAbsent(identification,"",idem.expireTime(),idem.timeunit());}return true;}private static void extracted(HttpServletResponse response) throws IOException {//告诉前端:请求是重复请求,请勿重复提交!ResponseResult res = ResponseResult.fail(BusinessEnum.HTTP_ALREADY_HANDLE);String s = JSONUtil.toJsonStr(res);response.setContentType("application/json;charset=utf-8");PrintWriter out = response.getWriter();out.write(s);}
}

配置拦截器

@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {//……@Beanpublic IdempotentInterceptor idempotentInterceptor(){return new IdempotentInterceptor();}//……@Overridepublic void addInterceptors(InterceptorRegistry registry) {//……//向SpringMVC中 注册 幂等拦截器   registry.addInterceptor(idempotentInterceptor()).addPathPatterns("/order/sumbitOrder/**");}//……
}

前端axios提交时,添加identification请求头:

在这里插入图片描述

submitOrder(){axios.post('/order/sumbitOrder/'+this.addressId+"/"+this.orderNum,this.cartVo,{headers: {'identification': this.identification, // 设置请求头的Content-Type为application/json}}).then(res=>{if(res.data.code == 200){this.$message.success("订单提交成功!");setTimeout(()=>{location.href = "/order.html";},3000)}else{this.$message.error(res.data.msg);}});
}

在这里插入图片描述

控制器上:添加@Idempotent 幂等注解

	@Idempotent(expireTime = 30,timeunit = TimeUnit.SECONDS)@PostMapping("/sumbitOrder/{addressId}/{orderNum}")public ResponseResult sumbitOrder(@RequestBody CartVo cartVo,@PathVariable("addressId") Integer addressId,@PathVariable("orderNum") String orderNum,@RequestHeader("Authorization") String token){//……}

缓存三兄弟:缓存击穿、穿透、雪崩

缓存击穿

缓存击穿:redis中没有,但是数据库有

顺序:先查缓存,判断缓存是否存在;如果缓存存在,直接返回数据;如果缓存不存在,則查询数据库,将数据库的数据存入到缓存

在这里插入图片描述

解决方案:将热点数据设置过期时间长一点;针对数据库的热点访问方法上分布式锁;

缓存穿透

缓存穿透:redis中没有,数据库也没有

在这里插入图片描述

解决方案:

(1)将不存在的key,在redis设置值为null;

(2)使用布隆过滤器;

原理:https://zhuanlan.zhihu.com/p/616911933

在这里插入图片描述

布隆过滤器:

如果确认key不存在于redis中,那么就一定不存在;

它说key存在,就有可能存在,也可能不存在! (误差)

在这里插入图片描述

布隆过滤器

1、根据配置类中的 key的数量 ,误差率,计算位图数组【二维数组】

2、通过布隆过滤器存放key的时候,会计算出需要多少个hash函数,由hash函数算出多少个位图位置需要设定为1

3、查询时,根据对应的hash函数,判断对应的位置值是否都为1;如果有位置为0,则表示key一定不存在于该redis服务器中;如果全部位置都为1,则表示key可能存在于redis服务器中;

缓存雪崩

缓存雪崩:

Redis的缓存雪崩是指当Redis中大量缓存数据同时失效或者被清空时,大量的请求会直接打到数据库上,导致数据库瞬时压力过大,甚至宕机的情况。

造成缓存雪崩的原因主要有两个:

1.相同的过期时间:当Redis中大量的缓存数据设置相同的过期时间时,这些数据很可能会在同一时间点同时失效,导致大量请求直接打到数据库上。

2.缓存集中失效:当服务器重启、网络故障等因素导致Redis服务不可用,且缓存数据没有自动进行容错处理,当服务恢复时大量的数据同时被重新加载到缓存中,也会导致大量请求直接打到数据库上。

预防缓存雪崩的方法主要有以下几种:

1.设置不同的过期时间:可以将缓存数据的过期时间分散开,避免大量缓存数据在同一时间点失效。

2.使用加锁:可以将所有请求都先进行加锁操作,当某个请求去查询数据库时,如果还没有加载到缓存中,则只让单个线程去执行加载操作,其他线程等待该线程完成后再次进行判断,避免瞬间都去访问数据库从而引起雪崩。

3.提前加载预热:在系统低峰期,可以提前将部分热点数据加载到缓存中,这样可以避免在高峰期缓存数据失效时全部打到数据库上。

4.使用多级缓存:可以在Redis缓存之上再使用一层缓存,例如本地缓存等,当Redis缓存失效时,还能够从本地缓存中获取数据,避免直接打到数据库上。

在这里插入图片描述

本地缓存:ehcache oscache spring自带缓存 持久层框架的缓存


总结

Redis的应用——接口幂等性,分布式锁,基于注解+拦截器的接口幂等改进

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

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

相关文章

c++学习记录 deque容器—构造函数

1、deque容器基本概念 1.1 功能 双端数组&#xff0c;可以对头端进行插入删除操作 1.2 deque与vector区别&#xff1a; vector对于头部的插入删除效率低&#xff0c;数据量越大&#xff0c;效率越低deque相对而言&#xff0c;对头部的插入删除速度比vector快vector访问元素…

vue中scss样式污染引发的思考

新做了一个项目&#xff0c;就是在登录后&#xff0c;就会产生左侧菜单的按钮颜色不一样。 然后发现样式是从这里传过来的 发现是登录页面的css给污染了 就是加了scope就把这个问题解决了 然后想总结一下这个思路&#xff1a;就是如何排查污染样式&#xff1a; 如果出现了…

东莞IBM服务器维修之IBM x3630 M4阵列恢复

记录东莞某抖音电商公司送修一台IBM SYSTEM X3630 M4文档服务器RAID6故障导致数据丢失的恢复案例 时间&#xff1a;2024年02月20日&#xff0c; 服务器品牌&#xff1a;IBM System x3630 M4&#xff0c;阵列卡用的是DELL PERC H730P 服务器用途和用户位置&#xff1a;某抖音电…

李沐《动手学深度学习》优化算法(相关概念、梯度下降法、牛顿法)

系列文章 李沐《动手学深度学习》预备知识 张量操作及数据处理 李沐《动手学深度学习》预备知识 线性代数及微积分 李沐《动手学深度学习》线性神经网络 线性回归 李沐《动手学深度学习》线性神经网络 softmax回归 李沐《动手学深度学习》多层感知机 模型概念和代码实现 李沐《…

二叉树(C/C++)

本篇将较为详细的介绍二叉树的相关知识&#xff0c;以及二叉树的实现。对于二叉树的相关知识&#xff0c;本篇介绍了其概念、特殊的二叉树、性质还有存储结构。 接着对于实现二叉树的每个函数都有其思路讲解&#xff0c;主要的函数分为&#xff1a;遍历&#xff1a;前中后序遍历…

【低代码开发_RuoYi_框架】RuoYi框架_前端页面部署/搭建

开源软件的影响力 随着信息技术的快速发展&#xff0c;开源软件已经成为软件开发的趋势&#xff0c;并产生了深远的影响。开源软件的低成本、可协作性和透明度等特点&#xff0c;使得越来越多的企业和个人选择使用开源软件&#xff0c;促进了软件行业的繁荣。然而&#xff0c;…

白酒:新工艺、新技术在白酒生产中的应用与展望

随着科技的不断发展&#xff0c;新工艺、新技术在豪迈白酒生产中的应用越来越广泛。这些新技术的应用&#xff0c;不仅提高了豪迈白酒的品质和生产效率&#xff0c;还为白酒产业的可持续发展提供了有力支持。 首先&#xff0c;新工艺在豪迈白酒生产中的应用具有重要意义。传统的…

Redis的BigKey

文章目录 1. 常见面试题2. MoreKey案例2. BigKey2. BigKey生产调优 1. 常见面试题 海量数据里查询某一个固定前缀的key&#xff1f;你如何生产上限制key * /flushdb /flushall等危险命令以防止误删误用&#xff1f;Memory Usage命令你用过吗&#xff1f;多大算BigKey&#xff…

双流机场到天府机场ADS-B数据导入MATLAB

MATLAB导入数据 导入的数据Excel部分截图&#xff1a; 一些处理 % 导入外部轨迹数据并转成标准形式 clear;clc; %% 导入&预处理 [NUM,TXT,RAW]xlsread(2021年10月31日CTU-TFU); time_cell RAW(3:end,1); %拉取时间数据&#xff08;cell&#xff09; time_char char(t…

php基础学习之错误处理(其二)

在实际应用中&#xff0c;开发者当然不希望把自己开发的程序的错误暴露给用户&#xff0c;一方面会动摇客户对己方的信心&#xff0c;另一方面容易被攻击者抓住漏洞实施攻击&#xff0c;同时开发者本身需要及时收集错误&#xff0c;因此需要合理的设置错误显示与记录错误日志 一…

最新开源!用C++编写的3D gaussian splatting

大家好&#xff0c;小柠檬给大家推荐一个用C编写的3D gaussian splatting的免费开源实现&#xff0c;专注于可移植、精益和快速。 OpenSplat采用相机姿势稀疏点&#xff0c;并计算一个场景文件&#xff08;.ply&#xff09;&#xff0c;稍后可以导入该文件以在其他软件中查看、…

在VMware中安装CentOS 7并配置Docker

VMware安装CentOS 7 一、介绍 该文章介绍如何使用启动U盘在虚拟机里面安装系统&#xff0c;虚拟机版本为VMware Workstation 16 pro&#xff0c;Linux版本为CentOS Linux release 7.9.2009 (Core)。 二、安装 1、创建虚拟机 点击创建新的虚拟机 选择典型就可以了&#xf…