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

在微服務中不少狀況下須要使用到分佈式鎖功能,而目前比較常見的方案是經過Redis來實現分佈式鎖,網上關於分佈式鎖的實現方式有不少,早期主要是基於Redisson等客戶端,但在Spring Boot2.x以上版本中使用Redis時,其客戶端庫已經默認使用lettuce。 因此本文將直接介紹在Spring Boot2.x以上項目中快速使用Redis分佈式鎖的功能的方法,但願可以更新你的知識庫!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算法是如何運行的,示意圖以下:spring

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

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

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

  • 互斥:任什麼時候刻只能有一個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子項目中,因此這裏引入其相關依賴。ide

2)編寫RedisLock的配置類,代碼以下:spring-boot

@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相關文章,學習資料都會在裏面更新,整理的資料也會放在裏面。

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

轉載自公衆號:mp.weixin.qq.com/s/AAq5Eu6x_…

相關文章
相關標籤/搜索