Redisson實現分布式鎖(3)—項目落地實現
有關Redisson實現分布式鎖前面寫了兩篇博客作為該項目落地的鋪墊。
2、Redisson實現分布式鎖(2)—RedissonLock
這篇講下通過Redisson實現分布式鎖的項目實現,我會把項目放到GitHub,該項目可以直接運用於實際開發中,作為分布式鎖使用。
一、項目概述
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");
}
好了,這篇博客就到這了!
至於完整的項目地址見GitHub。
如果對您能有幫助,就給個星星吧,哈哈!
GitHub地址
https://github.com/yudiandemingzi/spring-boot-distributed-redisson
參考
只要自己變優秀了,其他的事情才會跟着好起來(中將7)