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

 更新时间:2021年11月28日 15:16:13   作者:wb_csdn_job  
在系统中,有些接口如果重复提交,可能会造成脏数据或者其他的严重的问题,所以我们一般会对与数据库有交互的接口进行重复处理。本文就详细的介绍一下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分布式锁解决表单重复提交的问题的文章就介绍到这了,更多相关redis 表单重复提交内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 浅谈Java中Lambda表达式的相关操作

    浅谈Java中Lambda表达式的相关操作

    java8新特性,Lambda是一个匿名函数,类似Python中的Lambda表达式、js中的箭头函数,目的简化操作,文中有非常详细的介绍及代码示例,需要的朋友可以参考下
    2021-06-06
  • 解决@RequestBody搭配@Data的大坑

    解决@RequestBody搭配@Data的大坑

    这篇文章主要介绍了解决@RequestBody搭配@Data的大坑,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • Java程序生成Access文件代码实例

    Java程序生成Access文件代码实例

    这篇文章主要介绍了Java程序生成Access文件代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-09-09
  • Java中如何灵活获取excel中的数据

    Java中如何灵活获取excel中的数据

    这篇文章主要给大家介绍了关于Java中如何灵活获取excel中的数据,在日常工作中我们常常会进行文件读写操作,除去我们最常用的纯文本文件读写,更多时候我们需要对Excel中的数据进行读取操作,需要的朋友可以参考下
    2023-07-07
  • java实现Z字形扫描程序

    java实现Z字形扫描程序

    这篇文章主要为大家详细介绍了java实现Z字形扫描程序,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-03-03
  • Spring Security认证机制源码层探究

    Spring Security认证机制源码层探究

    SpringSecurity是基于Filter实现认证和授权,底层通过FilterChainProxy代理去调用各种Filter(Filter链),Filter通过调用AuthenticationManager完成认证 ,通过调用AccessDecisionManager完成授权
    2023-03-03
  • Java中TypeReference用法详情说明

    Java中TypeReference用法详情说明

    这篇文章主要介绍了Java中TypeReference用法详情说明,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-07-07
  • 面试中遇到的java逃逸分析问题

    面试中遇到的java逃逸分析问题

    这篇文章主要介绍了面试中遇到的java逃逸分析问题,逃逸分析(Escape Analysis)简单来讲就是,Java Hotspot 虚拟机可以分析新创建对象的使用范围,并决定是否在 Java 堆上分配内存的一项技术。,需要的朋友可以参考下
    2019-06-06
  • Java二叉树的四种遍历方式详解

    Java二叉树的四种遍历方式详解

    这篇文章主要介绍了Java二叉树的四种遍历,二叉树的遍历可以分为前序、中序、后序、层次遍历,需要的朋友可以参考下
    2021-11-11
  • Netty分布式ByteBuf使用SocketChannel读取数据过程剖析

    Netty分布式ByteBuf使用SocketChannel读取数据过程剖析

    这篇文章主要为大家介绍了Netty源码分析ByteBuf使用SocketChannel读取数据过程,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-03-03

最新评论