中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

redis分布式鎖如何解決表單重復提交的問題

發布時間:2021-11-29 09:07:56 來源:億速云 閱讀:169 作者:柒染 欄目:開發技術

本篇文章為大家展示了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

灵宝市| 密山市| 甘德县| 大方县| 当雄县| 宁城县| 沁水县| 金山区| 洪泽县| 赣榆县| 梅河口市| 无极县| 绥江县| 海淀区| 青海省| 江油市| 鱼台县| 博白县| 武邑县| 巴塘县| 樟树市| 临洮县| 安远县| 礼泉县| 昌平区| 阳泉市| 剑阁县| 营山县| 凭祥市| 苍梧县| 绿春县| 石棉县| 衡阳县| 青川县| 梁山县| 福安市| 颍上县| 卢湾区| 潞城市| 香格里拉县| 蚌埠市|