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

网友投稿 1236 2022-11-19

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);

}

http:// }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")));

}

}

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:C语言-函数的指针/函数指针/回调函数
下一篇:C语言-指针的比较/指针加减
相关文章

 发表评论

暂时没有评论,来抢沙发吧~