温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

redis分布式锁如何解决表单重复提交的问题

发布时间:2021-11-29 09:07:56 来源:亿速云 阅读:223 作者:柒染 栏目:开发技术

本篇文章为大家展示了redis分布式锁如何解决表单重复提交的问题,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。

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

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

1.引入redis依赖和aop依赖

<dependency>             <groupId>org.springframework.boot</groupId>             <artifactId>spring-boot-starter-redis</artifactId>             <version>1.3.8.RELEASE</version>         </dependency>	<dependency>                 <groupId>org.springframework.boot</groupId>                 <artifactId>spring-boot-starter-aop</artifactId>         </dependency>

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分布式锁如何解决表单重复提交的问题,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注亿速云行业资讯频道。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI