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

Posted wx5a20cf699eb6f

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了简单介绍redis分布式锁解决表单重复提交的问题相关的知识,希望对你有一定的参考价值。

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

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

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

1.引入redis依赖和aop依赖

org.springframework.boot     spring-boot-starter-redis     1.3.8.RELEASE      org.springframework.boot     spring-boot-starter-aop

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

/**  * @author wangbin  * @description redis分布式锁  * @date 2019年09月20日  */ @Component public class RedisLock {       private final Logger logger = LoggerFactory.getLogger(RedisLock.class);       @Autowired     private StringRedisTemplate redisTemplate;       /**      * @author wangbin      * @description 进行加锁的操作(该方法是单线程运行的)      * @date 2019年09月20日      * @param key 某个方法请求url加上cookie中的用户身份使用md5加密生成      * @param value 当前时间+过期时间(10秒)      * @return true表示加锁成功   false表示未获取到锁      */     public boolean lock(String key,String value){         //加锁成功返回true         if(redisTemplate.opsForValue().setIfAbsent(key,value,10, TimeUnit.SECONDS)){             return true;         }         String currentValue = redisTemplate.opsForValue().get(key);         //加锁失败,再判断是否由于解锁失败造成了死锁的情况         if(StringUtils.isNotEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()){             //获取上一个锁的时间,并且重新设置锁             String oldValue = redisTemplate.opsForValue().getAndSet(key, value);             if(StringUtils.isNotEmpty(oldValue) && oldValue.equals(currentValue)){                 //设置成功,重新设置锁是保证了单线程的运行                 return true;             }         }         return false;     }       /**      * @author wangbin      * @description 进行解锁的操作      * @date 2019年09月20日      * @param key 某个方法请求url使用md5加密生成      * @param value 当前时间+过期时间      * @return      */     public void unLock(String key,String value){         try {             String currentValue = redisTemplate.opsForValue().get(key);             if(StringUtils.isNotEmpty(currentValue) && currentValue.equals(value)){                 redisTemplate.delete(key);             }         }catch (Exception e){             logger.error("redis分布式锁,解锁异常",e);         }     }         /**      * @author wangbin      * @description 进行解锁的操作      * @date 2019年09月20日      * @param key 某个方法请求url使用md5加密生成      * @return      */     public void unLock(String key){         try {             String currentValue = redisTemplate.opsForValue().get(key);             if(StringUtils.isNotEmpty(currentValue)){                 redisTemplate.delete(key);             }         }catch (Exception e){             logger.error("redis分布式锁,解锁异常",e);         }     } }

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

@Configuration public class LoginInterceptor extends HandlerInterceptorAdapter {     private final Logger logger = LoggerFactory.getLogger(LoginInterceptor.class);     //超时时间设置为10秒     private static final int timeOut = 10000;     @Autowired     private StringRedisTemplate stringRedisTemplate;     @Autowired     private RedisLock redisLock;     /**      * 在请求处理之前进行调用(Controller方法调用之前)      * 基于URL实现的拦截器      * @param request      * @param response      * @param handler      * @return      * @throws Exception      */     @Override     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {         String path = request.getServletPath();         if (path.matches(Constants.NO_INTERCEPTOR_PATH)) {             //不需要的拦截直接过             return true;         } else {             // 这写你拦截需要干的事儿,比如取缓存,SESSION,权限判断等             //判断是否是重复提交的请求             if(!redisLock.lock(DigestUtils.md5Hex(request.getRequestURI()+value),String.valueOf(System.currentTimeMillis()+timeOut))){                         logger.info("===========获取锁失败,该请求为重复提交请求");                         return false;              }             return true;         }     } }

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

/**  * @author wangbin  * @description 使用redis分布式锁解决表单重复提交的问题  * @date 2019年09月20日  */ @Aspect @Component public class RepeatedSubmit {       @Autowired     private RedisLock redisLock;       //定义切点     @Pointcut("execution(public * com.kunluntop.logistics.controller..*.*(..))")     public void pointcut(){       }       //在方法执行完成后释放锁     @After("pointcut()")     public void after(){         ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();         HttpServletRequest request = attributes.getRequest();         redisLock.unLock(DigestUtils.md5Hex(request.getRequestURI()+ CookieUtils.getCookie(request,"userkey")));     } }

到此这篇关于redis分布式锁解决表单重复提交的问题的文章就介绍到这了

本文地址:​​https://www.linuxprobe.com/redis-linux-like.html​

以上是关于简单介绍redis分布式锁解决表单重复提交的问题的主要内容,如果未能解决你的问题,请参考以下文章

防重复提交实现方案

token防止前端重复提交

Aop+Redis防止接口重复提交

Springboot 2.x 如何解决重复提交 (本地锁的实践)

SpringBoot--防止重复提交(锁机制---本地锁分布式锁)

SpringBoot接口+Redis解决用户重复提交问题