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

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

有關Redisson實現分佈式鎖前面寫了兩篇博客做爲該項目落地的鋪墊。html

一、Redisson實現分佈式鎖(1)---原理java

二、Redisson實現分佈式鎖(2)—RedissonLockgit

這篇講下經過Redisson實現分佈式鎖的項目實現,我會把項目放到GitHub,該項目能夠直接運用於實際開發中,做爲分佈式鎖使用。github

GitHub地址 https://github.com/yudiandemingzi/spring-boot-distributed-redissonweb

1、項目概述

一、技術架構

項目整體技術選型redis

SpringBoot2.1.5 + Maven3.5.4 + Redisson3.5.4 + lombok(插件)

二、加鎖方式

該項目支持 自定義註解加鎖常規加鎖 兩種模式spring

自定義註解加鎖架構

@DistributedLock(value="goods", leaseTime=5)
  public String lockDecreaseStock(){
    //業務邏輯
  }

常規加鎖併發

//一、加鎖
 redissonLock.lock("redisson", 10);
 //二、業務邏輯
 //三、解鎖
 redissonLock.unlock("redisson");

三、Redis部署方式

該項目支持四種Redis部署方式app

一、單機模式部署
二、集羣模式部署
三、主從模式部署
四、哨兵模式部署

該項目已經實現支持上面四種模式,你要採用哪一種只須要修改配置文件application.properties,項目代碼不須要作任何修改。

四、項目總體結構

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


2、測試

模擬1秒內100個線程請求接口,來測試結果是否正確。同時測試3中不一樣的鎖:lock鎖、trylock鎖、註解鎖。

一、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 "=================================";
    }

壓測結果

沒問題,不會超賣!

二、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 "===================================";
    }

測試結果

沒有問題 ,不會超賣!

三、註解鎖

/**
     * 模擬這個是商品庫存
     */
    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 "=================================";
    }

測試結果

沒有問題 ,不會超賣!

經過實驗能夠看出,經過這三種模式均可以實現分佈式鎖,而後呢?哪一個最優。


3、三種鎖的鎖選擇

觀點 最完美的就是lock鎖,由於

一、tryLock鎖是可能會跳過減庫存的操做,由於當過了等待時間尚未獲取鎖,就會返回false,這顯然很致命!

二、註解鎖只能用於方法上,顆粒度太大,知足不了方法內加鎖。

一、lock PK tryLock 性能的比較

模擬5秒內1000個線程分別去壓測這兩個接口,看報告結果!

1)lock鎖

壓測結果 1000個線程平均響應時間爲31324。吞吐量 14.7/sec

2)tryLock鎖

壓測結果 1000個線程平均響應時間爲28628。吞吐量 16.1/sec

這裏只是單次測試,有很大的隨機性。從當前環境單次測試來看,tryLock稍微高點。

二、常見異常 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");
        }

好了,這篇博客就到這了!


參考

一、本身寫分佈式鎖--基於redission

二、Redisson實現Redis分佈式鎖的N種姿式

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




只要本身變優秀了,其餘的事情纔會跟着好起來(中將7)
相關文章
相關標籤/搜索