Redisson分布式锁

coffee_baby發表於2024-02-29

Redisson分散式鎖

來自Githup官方文件的介紹:

Redisson是一個在Redis的基礎上實現的Java駐記憶體資料網格(In-Memory Data Grid)。它不僅提供了一系列的分散式的Java常用物件,還提供了許多分散式服務。其中包括(BitSet, Set, Multimap, SortedSet, Map, List, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, AtomicLong, CountDownLatch, Publish / Subscribe, Bloom filter, Remote service, Spring cache, Executor service, Live Object service, Scheduler service) Redisson提供了使用Redis的最簡單和最便捷的方法。Redisson的宗旨是促進使用者對Redis的關注分離(Separation of Concern),從而讓使用者能夠將精力更集中地放在處理業務邏輯上。

官方文件地址:https://github.com/redisson/redisson/wiki

中文文件地址:https://github.com/redisson/redisson/wiki/目錄

先講一下為什麼使用分散式鎖:

在傳統的單體應用中,我們可以使用Java併發處理相關的API(如ReentrantLock或synchronized)來實現對共享資源的互斥控制,確保在高併發情況下同一時間只有一個執行緒能夠執行特定方法。然而,隨著業務的發展,單體應用逐漸演化為分散式系統,多執行緒、多程序分佈在不同機器上,這導致了原有的單機部署下的併發控制策略失效。為了解決這一問題,我們需要引入一種跨JVM的互斥機制來管理共享資源的訪問,這就是分散式鎖所要解決的核心問題。

SpringBoot框架整合

1.引入依賴

        <dependency>
            <groupId>org.redisson</groupId>
            <artifactId>redisson-spring-boot-starter</artifactId>
            <version>3.15.5</version>
        </dependency> 

2.建立配置類

@Configuration
public class RedissonConfig {

    @Value("${spring.redis.host}")
    private String redisHost;

    @Value("${spring.redis.port}")
    private int redisPort;

    @Value("${spring.redis.password}")
    private String password;

    @Value("${spring.redis.database}")
    private int database;

    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer()
                .setAddress("redis://" + redisHost + ":" + redisPort)
                .setPassword(password)
                .setDatabase(database)
                .setConnectionMinimumIdleSize(10) // 連線池最小空閒連線數
                .setConnectionPoolSize(50) // 連線池最大連線數
                .setIdleConnectionTimeout(60000) // 執行緒超時時間
                .setConnectTimeout(60000) // 客戶端程式獲取redis連線超時時間
                .setTimeout(60000); // 響應超時時間
        return Redisson.create(config);
    }
}

叢集的配置參考:https://github.com/redisson/redisson/wiki/2.-配置方法#24-叢集模式

3.實際使用

    public  void checkAndLock5() {
        RLock redisson_lock = redissonClient.getLock("redisson_lock");
        try{
            redisson_lock.lock();
            // 1. 業務程式碼
            
        }finally {
            redisson_lock.unlock();
        }
    }

可重入鎖(Reentrant Lock)

基於Redis的Redisson分散式可重入鎖RLock Java物件實現了java.util.concurrent.locks.Lock介面。同時還提供了非同步(Async)反射式(Reactive)RxJava2標準的介面

RLock lock = redisson.getLock("anyLock");
// 最常見的使用方法
lock.lock();

大家都知道,如果負責儲存這個分散式鎖的Redisson節點當機以後,而且這個鎖正好處於鎖住的狀態時,這個鎖會出現鎖死的狀態。為了避免這種情況的發生,Redisson內部提供了一個監控鎖的看門狗,它的作用是在Redisson例項被關閉前,不斷的延長鎖的有效期。預設情況下,看門狗的檢查鎖的超時時間是30秒鐘,也可以透過修改Config.lockWatchdogTimeout來另行指定。

另外Redisson還透過加鎖的方法提供了leaseTime的引數來指定加鎖的時間。超過這個時間後鎖便自動解開了。

// 加鎖以後10秒鐘自動解鎖
// 無需呼叫unlock方法手動解鎖
lock.lock(10, TimeUnit.SECONDS);

// 嘗試加鎖,最多等待100秒,上鎖以後10秒自動解鎖
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
if (res) {
   try {
     ...
   } finally {
       lock.unlock();
   }
}

公平鎖(Fair Lock)

基於Redis的Redisson分散式可重入公平鎖也是實現了java.util.concurrent.locks.Lock介面的一種RLock物件。同時還提供了非同步(Async)反射式(Reactive)RxJava2標準的介面。它保證了當多個Redisson客戶端執行緒同時請求加鎖時,優先分配給先發出請求的執行緒。所有請求執行緒會在一個佇列中排隊,當某個執行緒出現當機時,Redisson會等待5秒後繼續下一個執行緒,也就是說如果前面有5個執行緒都處於等待狀態,那麼後面的執行緒會等待至少25秒。

RLock fairLock = redisson.getFairLock("anyLock");
// 最常見的使用方法
fairLock.lock();

大家都知道,如果負責儲存這個分散式鎖的Redis節點當機以後,而且這個鎖正好處於鎖住的狀態時,這個鎖會出現鎖死的狀態。為了避免這種情況的發生,Redisson內部提供了一個監控鎖的看門狗,它的作用是在Redisson例項被關閉前,不斷的延長鎖的有效期。預設情況下,看門狗的檢查鎖的超時時間是30秒鐘,也可以透過修改Config.lockWatchdogTimeout來另行指定。

另外Redisson還透過加鎖的方法提供了leaseTime的引數來指定加鎖的時間。超過這個時間後鎖便自動解開了。

// 10秒鐘以後自動解鎖
// 無需呼叫unlock方法手動解鎖
fairLock.lock(10, TimeUnit.SECONDS);

// 嘗試加鎖,最多等待100秒,上鎖以後10秒自動解鎖
boolean res = fairLock.tryLock(100, 10, TimeUnit.SECONDS);
...
fairLock.unlock();

聯鎖(MultiLock)

基於Redis的Redisson分散式聯鎖RedissonMultiLock物件可以將多個RLock物件關聯為一個聯鎖,每個RLock物件例項可以來自於不同的Redisson例項。

RLock lock1 = redissonInstance1.getLock("lock1");
RLock lock2 = redissonInstance2.getLock("lock2");
RLock lock3 = redissonInstance3.getLock("lock3");

RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3);
// 同時加鎖:lock1 lock2 lock3
// 所有的鎖都上鎖成功才算成功。
lock.lock();
...
lock.unlock();

大家都知道,如果負責儲存某些分散式鎖的某些Redis節點當機以後,而且這些鎖正好處於鎖住的狀態時,這些鎖會出現鎖死的狀態。為了避免這種情況的發生,Redisson內部提供了一個監控鎖的看門狗,它的作用是在Redisson例項被關閉前,不斷的延長鎖的有效期。預設情況下,看門狗的檢查鎖的超時時間是30秒鐘,也可以透過修改Config.lockWatchdogTimeout來另行指定。

另外Redisson還透過加鎖的方法提供了leaseTime的引數來指定加鎖的時間。超過這個時間後鎖便自動解開了。

RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3);
// 給lock1,lock2,lock3加鎖,如果沒有手動解開的話,10秒鐘後將會自動解開
lock.lock(10, TimeUnit.SECONDS);

// 為加鎖等待100秒時間,並在加鎖成功10秒鐘後自動解開
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
...
lock.unlock();

紅鎖(RedLock)

基於Redis的Redisson紅鎖RedissonRedLock物件實現了Redlock介紹的加鎖演算法。該物件也可以用來將多個RLock物件關聯為一個紅鎖,每個RLock物件例項可以來自於不同的Redisson例項。

RLock lock1 = redissonInstance1.getLock("lock1");
RLock lock2 = redissonInstance2.getLock("lock2");
RLock lock3 = redissonInstance3.getLock("lock3");

RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);
// 同時加鎖:lock1 lock2 lock3
// 紅鎖在大部分節點上加鎖成功就算成功。
lock.lock();
...
lock.unlock();

大家都知道,如果負責儲存某些分散式鎖的某些Redis節點當機以後,而且這些鎖正好處於鎖住的狀態時,這些鎖會出現鎖死的狀態。為了避免這種情況的發生,Redisson內部提供了一個監控鎖的看門狗,它的作用是在Redisson例項被關閉前,不斷的延長鎖的有效期。預設情況下,看門狗的檢查鎖的超時時間是30秒鐘,也可以透過修改Config.lockWatchdogTimeout來另行指定。

另外Redisson還透過加鎖的方法提供了leaseTime的引數來指定加鎖的時間。超過這個時間後鎖便自動解開了。

RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);
// 給lock1,lock2,lock3加鎖,如果沒有手動解開的話,10秒鐘後將會自動解開
lock.lock(10, TimeUnit.SECONDS);

// 為加鎖等待100秒時間,並在加鎖成功10秒鐘後自動解開
boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);
...
lock.unlock();

讀寫鎖(ReadWriteLock)

基於Redis的Redisson分散式可重入讀寫鎖RReadWriteLock Java物件實現了java.util.concurrent.locks.ReadWriteLock介面。其中讀鎖和寫鎖都繼承了RLock介面。

分散式可重入讀寫鎖允許同時有多個讀鎖和一個寫鎖處於加鎖狀態。

RReadWriteLock rwlock = redisson.getReadWriteLock("anyRWLock");
// 最常見的使用方法
rwlock.readLock().lock();
// 或
rwlock.writeLock().lock();

大家都知道,如果負責儲存這個分散式鎖的Redis節點當機以後,而且這個鎖正好處於鎖住的狀態時,這個鎖會出現鎖死的狀態。為了避免這種情況的發生,Redisson內部提供了一個監控鎖的看門狗,它的作用是在Redisson例項被關閉前,不斷的延長鎖的有效期。預設情況下,看門狗的檢查鎖的超時時間是30秒鐘,也可以透過修改Config.lockWatchdogTimeout來另行指定。

另外Redisson還透過加鎖的方法提供了leaseTime的引數來指定加鎖的時間。超過這個時間後鎖便自動解開了。

// 10秒鐘以後自動解鎖
// 無需呼叫unlock方法手動解鎖
rwlock.readLock().lock(10, TimeUnit.SECONDS);
// 或
rwlock.writeLock().lock(10, TimeUnit.SECONDS);

// 嘗試加鎖,最多等待100秒,上鎖以後10秒自動解鎖
boolean res = rwlock.readLock().tryLock(100, 10, TimeUnit.SECONDS);
// 或
boolean res = rwlock.writeLock().tryLock(100, 10, TimeUnit.SECONDS);
...
lock.unlock();

訊號量(Semaphore)

基於Redis的Redisson的分散式訊號量(Semaphore)Java物件RSemaphore採用了與java.util.concurrent.Semaphore相似的介面和用法。同時還提供了非同步(Async)反射式(Reactive)RxJava2標準的介面。

RSemaphore semaphore = redisson.getSemaphore("semaphore");
semaphore.acquire();
//或
semaphore.acquireAsync();
semaphore.acquire(23);
semaphore.tryAcquire();
//或
semaphore.tryAcquireAsync();
semaphore.tryAcquire(23, TimeUnit.SECONDS);
//或
semaphore.tryAcquireAsync(23, TimeUnit.SECONDS);
semaphore.release(10);
semaphore.release();
//或
semaphore.releaseAsync();

閉鎖(CountDownLatch)

基於Redisson的Redisson分散式閉鎖(CountDownLatch)Java物件RCountDownLatch採用了與java.util.concurrent.CountDownLatch相似的介面和用法。

RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
latch.trySetCount(1);
latch.await();

// 在其他執行緒或其他JVM裡
RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
latch.countDown();

相關文章