场景演示

假设有一个录入学生信息的功能,为了便于演示,要求不能有重名的学生,并且数据库对应字段没有做唯一限制.

    @GetMapping("/student/{name}")public Object reSubmitTest(@PathVariable String name){List<Student> allByName = repository.findByName(name);if (allByName != null && allByName.size() > 0) {return "姓名重复";}//便于测试假设都是18岁return repository.save(new Student(name, 18));}

学生表

Field Type Comment
id int(11) 自增
name varchar(20) 姓名
age int(5) 年龄

上面这段代码,如果什么都不做,100个请求同时进来会发生什么呢

  1. 模拟同时100个请求

  1. 发现重复插入了很多条数据

如何解决

在网上有很多处理重复提交的方案,大部分的逻辑都是利用redis的key的过期时间,让请求在一点时间内重复进入方法,直达key过期.这种方法有一个缺点,需要固定一个时间,这个时间设置长了浪费性能,设置短了起不到防止重复提交的作用,我觉得有更好的方案

单服务

  1. 定义一个注解,拦截需要处理的方法
    @Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface Resubmit {}
  1. 拦截处理
@Aspect
@Component
public class SubmissionAop {@Autowiredprivate HttpServletRequest request;private static ConcurrentHashMap<String,Integer> concurrentHashMap = new ConcurrentHashMap<>();@Pointcut("@annotation(Resubmit)")public void needCheckMethod() {}@Before("needCheckMethod()")public void checkMethod() {//requestId 的获取,确保同一个用户,同一个urlString requestId = request.getMethod() + request.getServletPath() + request.getHeader("token");try {check(requestId);request.setAttribute("__request_resubmit_need_release","need");} catch (Exception e) {//抛出异常后被统一异常处理,转化为返回信息返回给前端throw new RuntimeException("重复提交-上一个请求还未处理完");}}@After("needCheckMethod()")public void release(){if ("need".equals(String.valueOf(request.getAttribute("__request_resubmit_need_release")))) {String requestId = request.getMethod() + request.getServletPath() + request.getHeader("token");concurrentHashMap.remove(requestId);}}/**同步保证查询和设置是原子操作* @param requestId* @throws Exception*/private static synchronized void check(String requestId) throws Exception {if (concurrentHashMap.get(requestId) != null) {throw new Exception();}concurrentHashMap.put(requestId, 1);}
}

在请求进入方法前,加锁,往后的同一个请求(requestId相同)无法获取锁,就被判定为重复请求,抛出异常,等第一个请求调用完毕后再释放锁,这样一来,就不需要设定时间来限制访问

多服务/或redis

上面的aop方法,无法适用于多服务/集群,总体逻辑不变,适用redis来做分布式锁就行了,改造比较简单,如下

    @Autowiredprivate StringRedisTemplate redisTemplate;@Autowiredprivate HttpServletRequest request;@Pointcut("@annotation(Resubmit)")public void needCheckMethod() {}@Before("needCheckMethod()")public void checkMethod() {String requestId = request.getServletPath() + request.getHeader("token");Boolean absent = redisTemplate.opsForValue().setIfAbsent(requestId, "1",60, TimeUnit.SECONDS);Assert.notNull(absent,"");if (!absent) {throw new RuntimeException("重复提交-上一个请求还未处理完");}request.setAttribute("__request_resubmit_need_release","need");}@After("needCheckMethod()")public void release(){if ("need".equals(String.valueOf(request.getAttribute("__request_resubmit_need_release")))) {String requestId = request.getServletPath() + request.getHeader("token");redisTemplate.delete(requestId);}}
}

上面的 redisTemplate.opsForValue().setIfAbsent(requestId, "1",60, TimeUnit.SECONDS);是原子操作,这也是为什么使用它做分布式锁的原因, 其次这个方法需要redis版本在2.1以上,否则只能在同步方法中先setIfAbsent 再设置过期时间了. 这里设置过期时间的原因是,有可能第一个请求设置完锁后,redis出现问题,导致后面的请求一直无法获取锁,从而所有请求都被判定为重复请求.
最后 如果发现本文有需要改进的地方,或者你有更好的方案,欢迎留言交流

原文:https://www.jianshu.com/p/bb2a4808c7b9

spring项目使用redis分布式锁解决重复提交问题相关推荐

  1. 简单介绍redis分布式锁解决表单重复提交的问题

    在系统中,有些接口如果重复提交,可能会造成脏数据或者其他的严重的问题,所以我们一般会对与数据库有交互的接口进行重复处理.本文就详细的介绍一下redis分布式锁解决表单重复提交,感兴趣的可以了解一下 假 ...

  2. 07: redis分布式锁解决超卖问题

    07: redis分布式锁解决超卖问题 参考文章: (1)07: redis分布式锁解决超卖问题 (2)https://www.cnblogs.com/xiaonq/p/12328934.html 备 ...

  3. Redis分布式锁解决抢购问题

    转:https://segmentfault.com/a/1190000011421467 废话不多说,首先分享一个业务场景-抢购.一个典型的高并发问题,所需的最关键字段就是库存,在高并发的情况下每次 ...

  4. Spring Cache使用Redisson分布式锁解决缓存击穿问题

    文章目录 1 什么是缓存击穿 2 为什么要使用分布式锁 3 什么是Redisson 4 Spring Boot集成Redisson 4.1 添加maven依赖 4.2 配置yml 4.3 配置Redi ...

  5. SpringBoot 使用 Redis 分布式锁解决并发问题

    问题背景 现在的应用程序架构中,很多服务都是多副本运行,从而保证服务的稳定性.一个服务实例挂了,其他服务依旧可以接收请求.但是服务的多副本运行随之也会引来一些分布式问题,比如某个接口的处理逻辑是这样的 ...

  6. spring boot redisLock redis分布式锁

    分布式锁: 分布式锁是控制分布式系统或者不同系统之间共同访问资源的一种锁实现,如果不同的系统或同一个系统的不同主机之间共享了某个资源,往往需要互斥来防止彼此干扰来保证一致性. 需解决问题: 1.任意时 ...

  7. 探讨Redis分布式锁解决优惠券拼抢问题

    一.什么是分布式锁 分布式锁是控制不同系统之间访问共享资源的一种锁实现,如果不同的系统或同一个系统的不同主机之间共享了某个资源时,往往需要互斥来防止彼此干扰来确保数据一致性. 二.为什么需要分布式锁 ...

  8. redis mysql 解决超卖_Redis 分布式锁解决超卖问题

    Redis 分布式锁解决超卖问题 1,Redis 事物介绍 1. Redis 事物是可以一次执行多个命令, 本质是一组命令的集合. 2. 一个事务中的所有命令都会序列化, 按顺序串行化的执行而不会被其 ...

  9. 【使用Redis分布式锁实现优惠券秒杀功能】-Redis学习笔记05

    前言 本章节主要实现限时.限量优惠券秒杀功能,并利用分布式锁解决<超卖问题>.<一人一单问题>. 一.优惠券下单基本功能实现 1.功能介绍及流程图 2.代码实现 @Resour ...

最新文章

  1. 源码-0205-02--聊天布局
  2. c#写的贪吃蛇小游戏
  3. bzoj 1863 二分+dp check
  4. 笔记-计算机网络基础-综合布线系统
  5. 四参数坐标转换c++_手持GPS的三参数计算方法
  6. .Net笔试题 有答案
  7. MacBook创建docker私有仓库docker server gave HTTP response to HTTPS client 问题处理办法
  8. Eclipse-Mybatis-generatorConfig.xml
  9. 假日活动的背后,酷开网络再造OTT营销的“价值高地”
  10. Icode编程>>>图形化编程>>>1级训练场>>>重复执行练习【1】
  11. “百度开放云编程马拉松”成都赛区19件作品及团队介绍
  12. nmon监控工具使用(打开nmon文件出现  运行时错误13类型不匹配)
  13. 小孩子要学习时间管理吗
  14. xshell 批量创建.xsh会话文件
  15. 记一次搜狐畅游后台开发笔试
  16. 送书!我觉得说啥都不如送几本新书
  17. 考研英语(二)——简单句
  18. 微信小程序|使用小程序制作一个足球拼图小游戏
  19. 并行分布式计算 并行计算机体系结构
  20. RT-Thread线程实例

热门文章

  1. 【学术相关】中国计算机学会推荐中文科技期刊目录
  2. 【小白学PyTorch】18.TF2构建自定义模型
  3. 30万奖金等你拿,“信也科技杯”第五届数据解决方案应用大赛火热报名中!...
  4. 推荐搜索炼丹笔记:双塔模型在Airbnb搜索排名中的应用
  5. 【直播预告】天黑请闭眼,杭州终极狼人大奖赛正在查杀——见证 4 万现金大奖的诞生!...
  6. 一场性能当道 优化为王的沙龙盛宴
  7. 易创课堂武汉站-NTES@百位创业者智慧众筹
  8. 什么是二维数组?二维遍历?Java二维数组制作图片迷宫 使用如鹏游戏引擎制作窗口界面 附带压缩包下载,解压后双击start.bat启动...
  9. 关于mount在unix系统上
  10. css 文本属性详细总结