1. 程式人生 > >Redisson實現分散式鎖(3)—專案落地實現

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)