Redisson實現分散式鎖(3)—專案落地實現
Redisson實現分散式鎖(3)—專案落地實現
有關Redisson實現分散式鎖前面寫了兩篇部落格作為該專案落地的鋪墊。
1、Redisson實現分散式鎖(1)---原理
2、Redisson實現分散式鎖(2)—RedissonLock
這篇講下通過Redisson實現分散式鎖的專案實現,我會把專案放到GitHub,該專案可以直接運用於實際開發中,作為分散式鎖使用。
GitHub地址
https://github.com/yudiandemingzi/spring-boot-distributed-redisson
一、專案概述
1、技術架構
專案總體技術選型
SpringBoot2.1.5 + Maven3.5.4 + Redisson3.5.4 + lombok(外掛)
2、加鎖方式
該專案支援 自定義註解加鎖
和 常規加鎖
兩種模式
自定義註解加鎖
@DistributedLock(value="goods", leaseTime=5)
public String lockDecreaseStock(){
//業務邏輯
}
常規加鎖
//1、加鎖
redissonLock.lock("redisson", 10);
//2、業務邏輯
//3、解鎖
redissonLock.unlock("redisson");
3、Redis部署方式
該專案支援四種Redis部署方式
1、單機模式部署 2、叢集模式部署 3、主從模式部署 4、哨兵模式部署
該專案已經實現支援上面四種模式,你要採用哪種只需要修改配置檔案application.properties
,專案程式碼不需要做任何修改。
4、專案整體結構
redis-distributed-lock-core # 核心實現 | ---src | ---com.jincou.redisson |# 通過註解方式 實現分散式鎖 ---annotation |# 配置類例項化RedissonLock ---config |# 放置常量資訊 ---constant |# 讀取application.properties資訊後,封裝到實體 ---entity |# 支援單機、叢集、主從、哨兵 程式碼實現 ---strategy redis-distributed-lock-web-test # 針對上面實現類的測試類 | ---src | ---java | ---com.jincou.controller |# 測試 基於註解方式實現分散式鎖 ---AnnotatinLockController.java |# 測試 基於常規方式實現分散式鎖 ---LockController.java ---resources | # 配置埠號 連線redis資訊(如果確定部署型別,那麼將連線資訊放到core專案中) ---application.properties
二、測試
模擬1秒內100個執行緒
請求介面,來測試結果是否正確。同時測試3中不同的鎖:lock鎖、trylock鎖、註解鎖。
1、lock鎖
/**
* 模擬這個是商品庫存
*/
public static volatile Integer TOTAL = 10;
@GetMapping("lock-decrease-stock")
public String lockDecreaseStock() throws InterruptedException {
redissonLock.lock("lock", 10);
if (TOTAL > 0) {
TOTAL--;
}
Thread.sleep(50);
log.info("======減完庫存後,當前庫存===" + TOTAL);
//如果該執行緒還持有該鎖,那麼釋放該鎖。如果該執行緒不持有該鎖,說明該執行緒的鎖已到過期時間,自動釋放鎖
if (redissonLock.isHeldByCurrentThread("lock")) {
redissonLock.unlock("lock");
}
return "=================================";
}
壓測結果
沒問題,不會超賣!
2、tryLock鎖
/**
* 模擬這個是商品庫存
*/
public static volatile Integer TOTAL = 10;
@GetMapping("trylock-decrease-stock")
public String trylockDecreaseStock() throws InterruptedException {
if (redissonLock.tryLock("trylock", 10, 5)) {
if (TOTAL > 0) {
TOTAL--;
}
Thread.sleep(50);
redissonLock.unlock("trylock");
log.info("====tryLock===減完庫存後,當前庫存===" + TOTAL);
} else {
log.info("[ExecutorRedisson]獲取鎖失敗");
}
return "===================================";
}
測試結果
沒有問題 ,不會超賣!
3、註解鎖
/**
* 模擬這個是商品庫存
*/
public static volatile Integer TOTAL = 10;
@GetMapping("annotatin-lock-decrease-stock")
@DistributedLock(value="goods", leaseTime=5)
public String lockDecreaseStock() throws InterruptedException {
if (TOTAL > 0) {
TOTAL--;
}
log.info("===註解模式=== 減完庫存後,當前庫存===" + TOTAL);
return "=================================";
}
測試結果
沒有問題 ,不會超賣!
通過實驗可以看出,通過這三種模式都可以實現分散式鎖,然後呢?哪個最優。
三、三種鎖的鎖選擇
觀點
最完美的就是lock鎖,因為
1、tryLock鎖是可能會跳過減庫存的操作,因為當過了等待時間還沒有獲取鎖,就會返回false,這顯然很致命!
2、註解鎖只能用於方法上,顆粒度太大,滿足不了方法內加鎖。
1、lock PK tryLock 效能的比較
模擬5秒內1000個執行緒
分別去壓測這兩個介面,看報告結果!
1)lock鎖
壓測結果 1000個執行緒平均響應時間為31324。吞吐量 14.7/sec
2)tryLock鎖
壓測結果 1000個執行緒平均響應時間為28628。吞吐量 16.1/sec
這裡只是單次測試,有很大的隨機性。從當前環境單次測試來看,tryLock稍微高點。
2、常見異常 attempt to unlock lock, not ······
在使用RedissonLock鎖時,很容易報這類異常,比如如下操作
//設定鎖1秒過去
redissonLock.lock("redisson", 1);
/**
* 業務邏輯需要諮詢2秒
*/
redissonLock.release("redisson");
上面在併發情況下就會這樣
造成異常原因:
執行緒1 進來獲得鎖後,但它的業務邏輯需要執行2秒,在 執行緒1 執行1秒後,這個鎖就自動過期了,那麼這個時候
執行緒2 進來了獲得了鎖。線上程1去解鎖就會拋上面這個異常(因為解鎖和當前鎖已經不是同一執行緒了)
所以我們需要注意,設定鎖的過期時間不能設定太小,一定要合理,寧願設定大點。
正對上面的異常,可以通過isHeldByCurrentThread()方法,
//如果為false就說明該執行緒的鎖已經自動釋放,無需解鎖
if (redissonLock.isHeldByCurrentThread("lock")) {
redissonLock.unlock("lock");
}
好了,這篇部落格就到這了!
參考
1、自己寫分散式鎖--基於redission
2、Redisson實現Redis分散式鎖的N種姿勢
3、利用Redisson實現分散式鎖及其底層原理解析
只要自己變優秀了,其他的事情才會跟著好起來(中將7)