分散式鎖中的王者方案 - Redisson

網路上有各種關於Redis實作分散式鎖定的方案,但何為王者方案?
答案就: Redisson。
我們先來看看 Redis 官網對分散式鎖定的說法:

#而 Java 版的 分散式鎖定的框架就是 Redisson。
這篇實戰內容將會以我的開源專案 PassJava 為基礎來整合 Redisson。
我把後端
、前端
、小程式
都上傳到同一個倉庫裡面了,大家可以透過Github
或碼雲
存取。網址如下:
Github: https://github.com/Jackson0714/PassJava-Platform
碼雲#:https: //gitee.com/jayh2018/PassJava-Platform
配套教學:www.passjava.cn
在實戰前,我們先來看看使用Redisson 的原理。
一、Redisson 是什麼?
如果你之前是在用 Redis 的話,那使用 Redisson 的話將會事半功倍,Redisson 提供了使用 Redis的最簡單和最便捷的方法。
Redisson的宗旨是促進使用者對 Redis 的關注分離(Separation of Concern),從而讓使用者能夠將精力更集中地放在處理業務邏輯上。
Redisson 是一個在 Redis 的基礎上實作的 Java 駐記憶體資料網格(In-Memory Data Grid)。

Netty 框架:Redisson採用了基於NIO的Netty框架,不僅能作為Redis底層驅動客戶端,具備提供對Redis各種組態形式的連接功能,對Redis命令能以同步發送、非同步形式發送、非同步流形式發送或管道形式發送的功能,LUA腳本執行處理,以及處理返回結果的功能
基礎資料結構:將原生的Redis
Hash
,List
,Set
,String
,Geo
,HyperLogLog
等資料結構封裝為Java里大家最熟悉的映射(Map)
,列表(List)
,集(Set)
,通用物件桶(Object Bucket)
,地理空間物件桶(Geospatial Bucket)
,基數估計演算法(HyperLogLog)
等結構,#分散式資料結構:這基礎上也提供了分佈式的多值映射(Multimap),本地快取映射(LocalCachedMap),有序集(SortedSet),計分排序集(ScoredSortedSet),字典排序集(LexSortedSet),列隊(Queue),阻塞佇列(Blocking Queue),有界阻塞列隊(Bounded Blocking Queue),雙端隊列(Deque),阻塞雙端列隊(Blocking Deque),阻塞公平列隊(Blocking Fair Queue),延遲列隊(Delayed Queue),布隆過濾器(Bloom Filter) ,原子整長形(AtomicLong),原子雙精確度浮點數(AtomicDouble),BitSet等Redis原本沒有的分散式資料結構。
分散式鎖定:Redisson也實作了Redis文件中提到像是分散式鎖定
Lock
這樣的更高階應用場景。事實上Redisson並沒有不只步於此,在分散式鎖的基礎上還提供了聯鎖(MultiLock)
,讀寫鎖(ReadWriteLock)
,公平鎖(Fair Lock)
,紅鎖(RedLock)
,信號量(Semaphore)
,可過期性訊號量(PermitExpirableSemaphore)
和閉鎖(CountDownLatch)
這些實際當中對多執行緒高並發應用至關重要的基本元件。正是透過實現基於Redis的高階應用方案,使Redisson成為建構分散式系統的重要工具。節點:Redisson作為獨立節點可以用於獨立執行其他節點發佈到
分散式執行服務
和分散式調度服務
裡的遠端任務。
二、整合Redisson
#Spring Boot 整合Redisson 有兩種方案:
程式化配置。 檔案方式配置。
本篇介紹如何用程式化的方式整合 Redisson。
2.1 引入 Maven 依賴
在 passjava-question 微服務的 pom.xml 引入 redisson的 maven 依賴。
<!-- https://mvnrepository.com/artifact/org.redisson/redisson --> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.15.5</version> </dependency>
2.2 自訂設定類別
下面的程式碼是單節點 Redis 的設定。
@Configuration public class MyRedissonConfig { /** * 对 Redisson 的使用都是通过 RedissonClient 对象 * @return * @throws IOException */ @Bean(destroyMethod="shutdown") // 服务停止后调用 shutdown 方法。 public RedissonClient redisson() throws IOException { // 1.创建配置 Config config = new Config(); // 集群模式 // config.useClusterServers().addNodeAddress("127.0.0.1:7004", "127.0.0.1:7001"); // 2.根据 Config 创建出 RedissonClient 示例。 config.useSingleServer().setAddress("redis://127.0.0.1:6379"); return Redisson.create(config); } }
2.3 測試設定類別
新建一個單元測試方法。
@Autowired RedissonClient redissonClient; @Test public void TestRedisson() { System.out.println(redissonClient); }
我們執行這個測試方法,印出redissonClient
org.redisson.Redisson@77f66138
三、分散式可重入鎖定
3.1 可重入鎖定測試
基於Redis的Redisson分散式可重入鎖定RLock
Java 物件實作了java.util.concurrent.locks.Lock
介面。同時也提供了非同步(Async)、反射式(Reactive)和RxJava2標準的介面。
RLock lock = redisson.getLock("anyLock"); // 最常见的使用方法 lock.lock();
我們用passjava 這個開源專案測試下可重入鎖的兩個點:
(1)多個執行緒搶佔鎖,後面鎖需要等待嗎? (2)如果搶占到鎖的執行緒所在的服務停了,鎖會不會被釋放?
3.1.1 驗證一:可重入鎖定是阻塞的嗎?
為了驗證以上兩點,我寫了個demo 程式:程式碼的流程就是設定WuKong-lock
鎖,然後加鎖,列印線程ID,等待10 秒後釋放鎖,最後回傳回應:「test lock ok」。
@ResponseBody @GetMapping("test-lock") public String TestLock() { // 1.获取锁,只要锁的名字一样,获取到的锁就是同一把锁。 RLock lock = redisson.getLock("WuKong-lock"); // 2.加锁 lock.lock(); try { System.out.println("加锁成功,执行后续代码。线程 ID:" + Thread.currentThread().getId()); Thread.sleep(10000); } catch (Exception e) { //TODO } finally { lock.unlock(); // 3.解锁 System.out.println("Finally,释放锁成功。线程 ID:" + Thread.currentThread().getId()); } return "test lock ok"; }
先驗證第一個點,用兩個 http 請求來測試搶佔鎖定。
請求的 URL:
http://localhost:11000/question/v1/redisson/test/test-lock

第一个线程对应的线程 ID 为 86,10秒后,释放锁。在这期间,第二个线程需要等待锁释放。
第一个线程释放锁之后,第二个线程获取到了锁,10 秒后,释放锁。
画了一个流程图,帮助大家理解。如下图所示:

第一步:线程 A 在 0 秒时,抢占到锁,0.1 秒后,开始执行等待 10 s。 第二步:线程 B 在 0.1 秒尝试抢占锁,未能抢到锁(被 A 抢占了)。 第三步:线程 A 在 10.1 秒后,释放锁。 第四步:线程 B 在 10.1 秒后抢占到锁,然后等待 10 秒后释放锁。
由此可以得出结论,Redisson 的可重入锁(lock)是阻塞其他线程的,需要等待其他线程释放的。
3.1.2 验证二:服务停了,锁会释放吗?
如果线程 A 在等待的过程中,服务突然停了,那么锁会释放吗?如果不释放的话,就会成为死锁,阻塞了其他线程获取锁。
我们先来看下线程 A 的获取锁后的,Redis 客户端查询到的结果,如下图所示:

WuKong-lock 有值,而且大家可以看到 TTL 在不断变小,说明 WuKong-lock 是自带过期时间的。
通过观察,经过 30 秒后,WuKong-lock 过期消失了。说明 Redisson 在停机后,占用的锁会自动释放。

那这又是什么原理呢?这里就要提一个概念了,看门狗
。

3.2 看门狗原理
如果负责储存这个分布式锁的 Redisson 节点宕机以后,而且这个锁正好处于锁住的状态时,这个锁会出现锁死的状态。为了避免这种情况的发生,Redisson内部提供了一个监控锁的看门狗
,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期。
默认情况下,看门狗的检查锁的超时时间是30秒钟,也可以通过修改Config.lockWatchdogTimeout来另行指定。
如果我们未制定 lock 的超时时间,就使用 30 秒作为看门狗的默认时间。只要占锁成功,就会启动一个定时任务
:每隔 10 秒重新给锁设置过期的时间,过期时间为 30 秒。
如下图所示:

当服务器宕机后,因为锁的有效期是 30 秒,所以会在 30 秒内自动解锁。(30秒等于宕机之前的锁占用时间+后续锁占用的时间)。
如下图所示:

3.3 设置锁过期时间
我们也可以通过给锁设置过期时间,让其自动解锁。
如下所示,设置锁 8 秒后自动过期。
lock.lock(8, TimeUnit.SECONDS);
如果业务执行时间超过 8 秒,手动释放锁将会报错,如下图所示:

所以我们如果设置了锁的自动过期时间,则执行业务的时间一定要小于锁的自动过期时间,否则就会报错。
四、王者方案
上一篇我讲解了分布式锁的五种方案:Redis 分布式锁|从青铜到钻石的五种演进方案,这一篇主要是讲解如何用 Redisson 在 Spring Boot 项目中实现分布式锁的方案。
因为 Redisson 非常强大,实现分布式锁的方案非常简洁,所以称作王者方案
。
原理图如下:

代码如下所示:
// 1.设置分布式锁 RLock lock = redisson.getLock("lock"); // 2.占用锁 lock.lock(); // 3.执行业务 ... // 4.释放锁 lock.unlock();
和之前 Redis 的方案相比,简洁很多。
下面讲解下 Redisson 的其他几种分布式锁,相信大家在以后的项目中也会用到。
五、分布式读写锁
基于 Redis 的 Redisson 分布式可重入读写锁RReadWriteLock
Java对象实现了java.util.concurrent.locks.ReadWriteLock
接口。其中读锁和写锁都继承了 RLock
接口。
写锁是一个排他锁(互斥锁),读锁是一个共享锁。
读锁 + 读锁:相当于没加锁,可以并发读。 读锁 + 写锁:写锁需要等待读锁释放锁。 写锁 + 写锁:互斥,需要等待对方的锁释放。 写锁 + 读锁:读锁需要等待写锁释放。

示例代码如下:
RReadWriteLock rwlock = redisson.getReadWriteLock("anyRWLock"); // 最常见的使用方法 rwlock.readLock().lock(); // 或 rwlock.writeLock().lock();
另外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();
六、分布式信号量
基于Redis的Redisson的分布式信号量(Semaphore)Java对象RSemaphore
采用了与java.util.concurrent.Semaphore
相似的接口和用法。同时还提供了异步(Async)、反射式(Reactive)和RxJava2标准的接口。
关于信号量的使用大家可以想象一下这个场景,有三个停车位,当三个停车位满了后,其他车就不停了。可以把车位比作信号,现在有三个信号,停一次车,用掉一个信号,车离开就是释放一个信号。

我们用 Redisson 来演示上述停车位的场景。
先定义一个占用停车位的方法:
/** * 停车,占用停车位 * 总共 3 个车位 */ @ResponseBody @RequestMapping("park") public String park() throws InterruptedException { // 获取信号量(停车场) RSemaphore park = redisson.getSemaphore("park"); // 获取一个信号(停车位) park.acquire(); return "OK"; }
再定义一个离开车位的方法:
/** * 释放车位 * 总共 3 个车位 */ @ResponseBody @RequestMapping("leave") public String leave() throws InterruptedException { // 获取信号量(停车场) RSemaphore park = redisson.getSemaphore("park"); // 释放一个信号(停车位) park.release(); return "OK"; }
为了简便,我用 Redis 客户端添加了一个 key:“park”,值等于 3,代表信号量为 park,总共有三个值。

然后用 postman 发送 park 请求占用一个停车位。

然後在 redis 用戶端查看 park 的值,發現已經改為 2 了。繼續呼叫兩次,發現 park 的等於 0,當呼叫第四次的時候,會發現請求一直處於等待中
,說明車位不夠了。如果想要不阻塞,可以用 tryAcquire 或 tryAcquireAsync。
我們再來呼叫離開車位的方法,park 的值變成 1,代表車位剩餘 1 個。
注意:多次執行釋放信號量操作,剩餘訊號量會一直增加,而不是到 3 後就封頂了。
其他分散式鎖定:
公平鎖定(Fair Lock)
聯鎖(MultiLock)
紅鎖(RedLock)
讀寫鎖定( ReadWriteLock)
可過期性訊號量(PermitExpirableSemaphore)
#閉鎖(CountDownLatch)

還有其他分散式鎖定就不在本篇展開了,有興趣的同學可以查看官方文件。
以上是分散式鎖中的王者方案 - Redisson的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

熱門話題

使用場景1、下單成功,30分鐘未支付。支付超時,自動取消訂單2、訂單簽收,簽收後7天未進行評估。訂單超時未評價,系統預設好評3、下單成功,商家5分鐘未接單,訂單取消4、配送超時,推播簡訊提醒…對於延時比較長的場景、即時性不高的場景,我們可以採用任務調度的方式定時輪詢處理。如:xxl-job今天我們採

今天跟大家分享的是分散式鎖,本文使用五個案例、圖、源碼分析等來分析。常見的synchronized、Lock等這些鎖都是基於單一JVM的實現的,如果分佈式場景下怎麼辦呢?這時候分散式鎖就出現了。

隨著現代應用程式的不斷發展和對高可用性和並發性的需求日益增長,分散式系統架構變得越來越普遍。在分散式系統中,多個進程或節點同時運作並共同完成任務,進程之間的同步變得特別重要。由於分散式環境下許多節點可以同時存取共享資源,因此,在分散式系統中,如何處理並發和同步問題成為了一項重要的任務。在此方面,ZooKeeper已經成為了一個非常流行的解決方案。 ZooKee

所需的Mavenorg.springframework.bootspring-boot-starter-data-redisio.lettucelettuce-coreredis.clientsjedisorg.springframework.sessionspring-session-data-redisorg.redissonredisson3.17.5application-redis。 6379密碼

隨著分散式系統的逐漸普及,分散式鎖已成為確保系統穩定性和資料一致性的重要手段。 Redis作為一款高效能的分散式記憶體資料庫,自然成為了分散式鎖的重要實作之一。但是,最近幾年,Etcd作為新興的分散式一致性解決方案,受到了越來越多的關注。本文將從實作原理、比較分析等方面探討Redis實現分散式鎖與Etcd的異同。 Redis實現分散式鎖的原理Redis分散式鎖的實

如果你之前是在用 Redis 的話,那使用 Redisson 的話將會事半功倍,Redisson 提供了使用 Redis的最簡單和最便捷的方法。 Redisson的宗旨是促進使用者對 Redis 的關注分離(Separation of Concern),讓使用者能夠將精力更集中地放在處理業務邏輯上。

Redisson是一種基於Redis的Java應用程式快取解決方案。它提供了許多有用功能,使得在Java應用程式中使用Redis作為快取變得更加方便和高效。 Redisson提供的快取功能包括:1.分散式映射(Map):Redisson提供了一些用於建立分散式映射的API。這些映射可以包含鍵值對、哈希表項或對象,它們可以支援在多個節點之間共

隨著網路的快速發展,網站訪問量的急劇增加,分散式系統的重要性也逐漸凸顯出來。在分散式系統中,不可避免地涉及到並發同步以及資料一致性的問題。而分散式鎖,作為一種解決並發同步問題的手段,也逐漸被廣泛應用於分散式系統中。在PHP中,可以利用Redis實現分散式鎖,本文將對此進行介紹。什麼是分散式鎖?在分散式系統中,當多個機器共同處理同一個任務時,為了避免出現多個機
