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 表单重复提交内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!

(0)
上一篇 2022年3月21日
下一篇 2022年3月21日

相关推荐