Redisson實現分布式鎖(3)—項目落地實現


Redisson實現分布式鎖(3)—項目落地實現

有關Redisson實現分布式鎖前面寫了兩篇博客作為該項目落地的鋪墊。

1、Redisson實現分布式鎖(1)---原理

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

參考

1、自己寫分布式鎖--基於redission

2、Redisson實現Redis分布式鎖的N種姿勢

3、利用Redisson實現分布式鎖及其底層原理解析



只要自己變優秀了,其他的事情才會跟着好起來(中將7)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM