千家信息网

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

发表于:2025-11-18 作者:千家信息网编辑
千家信息网最后更新 2025年11月18日,本篇文章为大家展示了redis分布式锁如何解决表单重复提交的问题,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。假如用户的网速慢,用户点击提交按钮,却因为网速慢
千家信息网最后更新 2025年11月18日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日 */@Componentpublic 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.使用拦截器在请求之前进行加锁的判断。

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

0