经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 数据库/运维 » Redis » 查看文章
redis分布式锁解决表单重复提交的问题
来源:jb51  时间:2021/11/29 9:29:49  对本文有异议

假如用户的网速慢,用户点击提交按钮,却因为网速慢,而没有跳转到新的页面,这时的用户会再次点击提交按钮,举个例子:用户点击订单页面,当点击提交按钮的时候,也许因为网速的原因,没有跳转到新的页面,这时的用户会再次点击提交按钮,如果没有经过处理的话,这时用户就会生成两份订单,类似于这种场景都叫重复提交。

使用redis的setnx和getset命令解决表单重复提交的问题。

1.引入redis依赖和aop依赖

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-redis</artifactId>
  4. <version>1.3.8.RELEASE</version>
  5. </dependency>
  6.  
  7. <dependency>
  8. <groupId>org.springframework.boot</groupId>
  9. <artifactId>spring-boot-starter-aop</artifactId>
  10. </dependency>

2.编写加锁和解锁的方法。

  1. /**
  2. * @author wangbin
  3. * @description redis分布式锁
  4. * @date 2019年09月20日
  5. */
  6. @Component
  7. public class RedisLock {
  8.  
  9. private final Logger logger = LoggerFactory.getLogger(RedisLock.class);
  10.  
  11. @Autowired
  12. private StringRedisTemplate redisTemplate;
  13.  
  14. /**
  15. * @author wangbin
  16. * @description 进行加锁的操作(该方法是单线程运行的)
  17. * @date 2019年09月20日
  18. * @param key 某个方法请求url加上cookie中的用户身份使用md5加密生成
  19. * @param value 当前时间+过期时间(10秒)
  20. * @return true表示加锁成功 false表示未获取到锁
  21. */
  22. public boolean lock(String key,String value){
  23. //加锁成功返回true
  24. if(redisTemplate.opsForValue().setIfAbsent(key,value,10, TimeUnit.SECONDS)){
  25. return true;
  26. }
  27. String currentValue = redisTemplate.opsForValue().get(key);
  28. //加锁失败,再判断是否由于解锁失败造成了死锁的情况
  29. if(StringUtils.isNotEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()){
  30. //获取上一个锁的时间,并且重新设置锁
  31. String oldValue = redisTemplate.opsForValue().getAndSet(key, value);
  32. if(StringUtils.isNotEmpty(oldValue) && oldValue.equals(currentValue)){
  33. //设置成功,重新设置锁是保证了单线程的运行
  34. return true;
  35. }
  36. }
  37. return false;
  38. }
  39.  
  40. /**
  41. * @author wangbin
  42. * @description 进行解锁的操作
  43. * @date 2019年09月20日
  44. * @param key 某个方法请求url使用md5加密生成
  45. * @param value 当前时间+过期时间
  46. * @return
  47. */
  48. public void unLock(String key,String value){
  49. try {
  50. String currentValue = redisTemplate.opsForValue().get(key);
  51. if(StringUtils.isNotEmpty(currentValue) && currentValue.equals(value)){
  52. redisTemplate.delete(key);
  53. }
  54. }catch (Exception e){
  55. logger.error("redis分布式锁,解锁异常",e);
  56. }
  57. }
  58.  
  59.  
  60. /**
  61. * @author wangbin
  62. * @description 进行解锁的操作
  63. * @date 2019年09月20日
  64. * @param key 某个方法请求url使用md5加密生成
  65. * @return
  66. */
  67. public void unLock(String key){
  68. try {
  69. String currentValue = redisTemplate.opsForValue().get(key);
  70. if(StringUtils.isNotEmpty(currentValue)){
  71. redisTemplate.delete(key);
  72. }
  73. }catch (Exception e){
  74. logger.error("redis分布式锁,解锁异常",e);
  75. }
  76. }
  77. }

3.使用拦截器在请求之前进行加锁的判断。

  1. @Configuration
  2. public class LoginInterceptor extends HandlerInterceptorAdapter {
  3. private final Logger logger = LoggerFactory.getLogger(LoginInterceptor.class);
  4. //超时时间设置为10秒
  5. private static final int timeOut = 10000;
  6. @Autowired
  7. private StringRedisTemplate stringRedisTemplate;
  8. @Autowired
  9. private RedisLock redisLock;
  10. /**
  11. * 在请求处理之前进行调用(Controller方法调用之前)
  12. * 基于URL实现的拦截器
  13. * @param request
  14. * @param response
  15. * @param handler
  16. * @return
  17. * @throws Exception
  18. */
  19. @Override
  20. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  21. String path = request.getServletPath();
  22. if (path.matches(Constants.NO_INTERCEPTOR_PATH)) {
  23. //不需要的拦截直接过
  24. return true;
  25. } else {
  26. // 这写你拦截需要干的事儿,比如取缓存,SESSION,权限判断等
  27. //判断是否是重复提交的请求
  28. if(!redisLock.lock(DigestUtils.md5Hex(request.getRequestURI()+value),String.valueOf(System.currentTimeMillis()+timeOut))){
  29. logger.info("===========获取锁失败,该请求为重复提交请求");
  30. return false;
  31. }
  32. return true;
  33. }
  34. }
  35. }

4.使用aop在后置通知中进行解锁。

  1. /**
  2. * @author wangbin
  3. * @description 使用redis分布式锁解决表单重复提交的问题
  4. * @date 2019年09月20日
  5. */
  6. @Aspect
  7. @Component
  8. public class RepeatedSubmit {
  9.  
  10. @Autowired
  11. private RedisLock redisLock;
  12.  
  13. //定义切点
  14. @Pointcut("execution(public * com.kunluntop.logistics.controller..*.*(..))")
  15. public void pointcut(){
  16.  
  17. }
  18.  
  19. //在方法执行完成后释放锁
  20. @After("pointcut()")
  21. public void after(){
  22. ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
  23. HttpServletRequest request = attributes.getRequest();
  24. redisLock.unLock(DigestUtils.md5Hex(request.getRequestURI()+ CookieUtils.getCookie(request,"userkey")));
  25. }
  26. }

到此这篇关于redis分布式锁解决表单重复提交的问题的文章就介绍到这了,更多相关redis 表单重复提交内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号