日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費收錄網(wǎng)站服務(wù),提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

作者:冰峰編者說:比較實用的redis加鎖的方式,代碼段可以收藏。

在最近的一次業(yè)務(wù)升級中,遇到這樣一個問題,我們設(shè)計了新的賬戶體系,需要在用戶將應(yīng)用升級之后將原來賬戶的數(shù)據(jù)手動的同步過來,就是需要用戶自己去觸發(fā)同步按鈕進(jìn)行同步,因為有些數(shù)據(jù)是用戶存在自己本地的。那么在這個過程中就存在一個問題,要是因為網(wǎng)絡(luò)的問題,用戶重復(fù)點擊了這個按鈕怎么辦?就算我們在客戶端做了一些處理,在同步的過程中,不能再次點擊,但是經(jīng)過我最近的爬蟲實踐,要是別人抓到了我們的接口那么還是不安全的。

基于這樣的業(yè)務(wù)場景,我就使用Redis加鎖的方式,限制了用戶在請求的時候,不能發(fā)起二次請求。

 

Redis如何保證接口的冪等性?我前后看了10遍,果斷收藏

我們在進(jìn)入請求之后首選嘗試獲取鎖對象,那么這個鎖對象的鍵其實就是用戶的id,如果獲取成功,我們判斷用戶時候已經(jīng)同步數(shù)據(jù),如果已同步,那么可以直接返回,提示用戶已經(jīng)同步,如果沒有那么直接執(zhí)行同步數(shù)據(jù)的業(yè)務(wù)邏輯,最后將鎖釋放,如果在進(jìn)入方法之后獲取鎖失敗,那么有可能就是在第一次請求還沒有結(jié)束的時候,接著又發(fā)起了請求,那么這個時候是獲取不到鎖的,也就不會發(fā)生數(shù)據(jù)同步出現(xiàn)同步好幾次的情況。

華麗的分割線

那么有了這個需求之后,我們就來用Redis實現(xiàn)以下這個代碼。首先我們要知道我們要介紹一下Redis的一個方法。

那么我們想要用Redis做用戶唯一的鎖對象,那么它在Redis中應(yīng)該是唯一的,而且還不應(yīng)該被覆蓋,這個方法就是存儲成功之后會返回true,如果該元素已經(jīng)存在于Redis實例中,那么直接返回false

setIfAbsent(key,value)

但是這中間又存在一個問題,如果在獲取了鎖對象之后,我們的服務(wù)掛了,那么這個時候其他請求肯定是拿不到鎖的,基于這種情況的考慮我們還應(yīng)該給這個元素添加一個過期時間,防止我們的服務(wù)掛掉之后,出現(xiàn)死鎖的問題。

/**
 * 添加元素
 *
 * @param key
 * @param value
 */
public void set(Object key, Object value) {
 if (key == null || value == null) {
 return;
 }
 redisTemplate.opsForValue().set(key, value.toString());
}
/**
 * 如果已經(jīng)存在返回false,否則返回true
 *
 * @param key
 * @param value
 * @return
 */
public Boolean setNx(Object key, Object value, Long expireTime, TimeUnit mimeUnit) {
 if (key == null || value == null) {
 return false;
 }
 return redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, mimeUnit);
}
/**
 * 獲取數(shù)據(jù)
 *
 * @param key
 * @return
 */
public Object get(Object key) {
 if (key == null) {
 return null;
 }
 return redisTemplate.opsForValue().get(key);
}
/**
 * 刪除
 *
 * @param key
 * @return
 */
public Boolean remove(Object key) {
 if (key == null) {
 return false;
 }
 return redisTemplate.delete(key);
}
/**
 * 加鎖
 *
 * @param key 
 * @param waitTime 等待時間
 * @param expireTime 過期時間
 */
public Boolean lock(String key, Long waitTime, Long expireTime) {
 String value = UUID.randomUUID().toString().replaceAll("-", "").toLowerCase();
 Boolean flag = setNx(key, value, expireTime, TimeUnit.MILLISECONDS);
 // 嘗試獲取鎖 成功返回
 if (flag) {
 return flag;
 } else {
 // 獲取失敗
 // 現(xiàn)在時間
 long newTime = System.currentTimeMillis();
 // 等待過期時間
 long loseTime = newTime + waitTime;
 // 不斷嘗試獲取鎖成功返回
 while (System.currentTimeMillis() < loseTime) {
 Boolean testFlag = setNx(key, value, expireTime, TimeUnit.MILLISECONDS);
 if (testFlag) {
 return testFlag;
 }
 try {
 Thread.sleep(1000);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
 }
 return false;
}
/**
 * 釋放鎖
 *
 * @param key
 * @return
 */
public Boolean unLock(Object key) {
 return remove(key);
}

我們整個加鎖的代碼邏輯已經(jīng)寫完了,我們來分析一下,用戶在進(jìn)來之后,首先調(diào)用lock嘗試獲取鎖,并進(jìn)行加鎖,lock()方法有三個參數(shù)分別是:key,waitTime就是用戶如果獲取不到鎖,可以等待多久,過了這個時間就不再等待,最后一個參數(shù)就是該鎖的多久后過期,防止服務(wù)掛了之后,發(fā)生死鎖。

當(dāng)進(jìn)入lock()之后,先進(jìn)行加鎖操作,如果加鎖成功,那么返回true,再執(zhí)行我們后面的業(yè)務(wù)邏輯,如果獲取鎖失敗,會獲取當(dāng)前時間再加上設(shè)置的過期時間,跟當(dāng)前時間比較,如果還在等待時間內(nèi),那么就再次嘗試獲取鎖,直到過了等待時間。

注意:在設(shè)置值的時候,我們?yōu)榱朔乐顾梨i設(shè)置了一個過期時間,大家一定要注意,不要等設(shè)置成功之后再去給元素設(shè)置過期時間,因為這個過程不是一個原子操作,等你剛設(shè)置成功之后,還沒等設(shè)置過期時間成功,服務(wù)直接掛了,那么這個時候就會發(fā)生死鎖問題,所以大家要保證存儲元素和設(shè)置過期時間一定要是原子操作。

最后我們來寫個測試類測試一下

@Test

public void test01() {

String key = "uid:12011";

Boolean flag = redisUtil.lock(key, 10L, 1000L * 60);

if (!flag) {

// 獲取鎖失敗

System.err.println("獲取鎖失敗");

} else {

// 獲取鎖成功

System.out.println("獲取鎖成功");

}

// 釋放鎖

redisUtil.unLock(key);

}

分享到:
標(biāo)簽:Redis
用戶無頭像

網(wǎng)友整理

注冊時間:

網(wǎng)站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨大挑戰(zhàn)2018-06-03

數(shù)獨一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運動步數(shù)有氧達(dá)人2018-06-03

記錄運動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績評定2018-06-03

通用課目體育訓(xùn)練成績評定