收藏慢慢看系列:簡潔實用的Redis分佈式鎖用法

收藏慢慢看系列:簡潔實用的Redis分佈式鎖用法

在微服務中很多情況下需要使用到分佈式鎖功能,而目前比較常見的方案是通過Redis來實現分佈式鎖,網上關於分佈式鎖的實現方式有很多,早期主要是基於Redisson等客戶端,但在Spring Boot2.x以上版本中使用Redis時,其客戶端庫已經默認使用lettuce。
所以本文將直接介紹在Spring Boot2.x以上項目中快速使用Redis分佈式鎖的功能的方法,希望能夠更新你的知識庫!

Redis分佈式鎖原理概述

實際上Redis服務本身並不提供分佈式鎖這樣的機制,但是作爲全局Key-Value存儲系統,客戶端可以利用Redis提供的基本功能並通過一定的算法設計來實現分佈式鎖功能。目前已有不少博客文章及代碼庫描述瞭如何使用Redis來實現分佈式鎖,但是許多實現相對比較簡單,安全性也比較低。在Redis的官方文檔中推薦了一種叫做RedLock的算法來實現基於Redis的分佈式鎖功能,現階段已存在基於該算法的多種語言版本的Redis客戶端實現庫。其中Java領域最爲知名的是Redisson庫。但由於Redisson不僅實現了分佈式鎖功能,還額外實現了一套Redis分佈式數據結構,因此會顯得比較重,加上最新的基於Spring Boot.2.x以上版本使用Redis時,其客戶端庫已經默認使用了lettuce(比Redisson、Jedis線程更安全、更輕量級的一種Java Redis客戶端庫)的封裝,所以爲了更加符合微服務場景下的使用,在實踐中往往會選擇基於RedLock算法自行實現分佈式鎖。

本案例也將演示如何RedLock算法來實現Redis分佈式鎖功能,不過在此之前讓我們先來看看RedLock算法是如何運行的,示意圖如下:

收藏慢慢看系列:簡潔實用的Redis分佈式鎖用法

以上就是實現Redis分佈式鎖官方推薦的RedLock算法邏輯,它是一種多節點Redis的分佈式鎖算法,可以有效防止單節點故障問題。其執行步驟說明如下:

  • 首先Redis客戶端獲取當前系統時間,以毫秒爲單位;
  • 然後客戶端會順序地嘗試向Redis集羣中的每個節點獲取鎖,其具體步驟是使用相同的鍵Key名和隨機值;在向每個Redis節點獲取鎖的過程中,客戶端會以比鎖過期時間小得多的時間來設定超時機制,例如鎖的整個超時時間爲10秒,集羣有5個節點,那麼每個節點獲取鎖的超時時間可能會被限制在5~50毫秒之間,這是爲了防止在某個節點不可用的情況下,客戶端等待時間過長,造成性能阻塞;
  • 之後隨着各節點獲取鎖結果的反饋,Redis客戶端會對獲取情況進行判斷,如果獲取各節點鎖的總時間小於鎖的超時時間設置,並且成功獲取鎖的節點數目大於N/2+1個(例如5個節點至少要有3個節點成功獲取鎖),滿足上述條件的情況下,Redis客戶端纔會認爲獲取鎖成功,否則就會認爲鎖獲取失敗,並依次釋放掉各個節點的鎖信息;
  • 獲取鎖成功後即可以安全地執行操作,完成後再依次釋放各節點鎖持有的鎖信息;

實現上述算法的Redis客戶端可以基本上保證分佈式鎖的有效性及安全性的幾個基本特性要求:

  • 互斥:任何時刻只能有一個Redis客戶端獲取鎖;
  • 無死鎖:即使鎖定資源的服務崩潰或者分區,仍然能釋放鎖);
  • 容錯性:只要多數redis節點(一半以上)在使用,Redis客戶端就可以獲取和釋放鎖;

Spring Boot集成使用方式

通過前面內容的描述,相信你對實現Redis分佈式鎖的基本算法應該有了一定的認識和理解。而在實踐的過程中可以依據該算法自行定製實現,但實際上Spring早就提供了基於該算法的Redis的分佈式鎖的實現。其具體使用步驟如下:

1)在工程pom.xml文件中引入Spring Integration依賴,代碼如下:

<!-- spring integration -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<!-- spring integration與redis結合,實現redis分佈式鎖 -->
<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-redis</artifactId>
</dependency>

目前Spring所提供的分佈式鎖相關的代碼被遷移在Spring Integration子項目中,所以這裏引入其相關依賴。

2)編寫RedisLock的配置類,代碼如下:

@Configuration
public class RedisLockConfiguration {
    @Bean
    public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory) {
        return new RedisLockRegistry(redisConnectionFactory, "payment");
    }
}

以上配置代碼加載的前提在於應用已經集成了Redis服務訪問鏈接信息,具體Spring Boot項目集成Redis訪問的方式比較簡單可以參考其他資料。

3)分佈式鎖的具體使用方式,代碼片段如下:

/**
 * 引入Redis分佈式鎖依賴組件
 */
@Autowired
private RedisLockRegistry redisLockRegistry;

@Override
public UnifiedPayBO unifiedPay(UnifiedPayDTO unifiedPayDTO) {
    ...
    //創建Redis分佈式鎖
    Lock lock = redisLockRegistry.obtain(redisLockPrefix + unifiedPayDTO.getOrderId());
    try {      
        //嘗試獲取鎖
        boolean isLock = lock.tryLock(1, TimeUnit.SECONDS);
        if (isLock) {
            //執行業務邏輯
            ...
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        //釋放分佈式鎖
        lock.unlock();
    }
    ...
}

上述代碼爲訂單防重時使用Redis分佈鎖的示例代碼,通過依賴注入RedisLockRegistry實例來實現分佈式鎖的相關操作,例如obtain()方法創建鎖、tryLock()持有鎖及unlock()釋放鎖等。

寫在最後:

歡迎大家關注我新開通的公衆號【風平浪靜如碼】,海量Java相關文章,學習資料都會在裏面更新,整理的資料也會放在裏面。

覺得寫的還不錯的就點個贊,加個關注唄!點關注,不迷路,持續更新!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章