Redisson實(shí)現(xiàn)Redis分布式鎖的幾種方式
前幾天發(fā)的一篇文章《Redlock:Redis分布式鎖最牛逼的實(shí)現(xiàn)》,引起了一些同學(xué)的討論,也有一些同學(xué)提出了一些疑問(wèn),這是好事兒。本文在講解如何使用Redisson實(shí)現(xiàn)Redis普通分布式鎖,以及Redlock算法分布式鎖的幾種方式的同時(shí),也附帶解答這些同學(xué)的一些疑問(wèn)。
Redis幾種架構(gòu)
Redis發(fā)展到現(xiàn)在,幾種常見的部署架構(gòu)有:
- 單機(jī)模式;
- 主從模式;
- 哨兵模式;
- 集群模式;
我們首先基于這些架構(gòu)講解Redisson普通分布式鎖實(shí)現(xiàn),需要注意的是,只有充分了解普通分布式鎖是如何實(shí)現(xiàn)的,才能更好的了解Redlock分布式鎖的實(shí)現(xiàn),因?yàn)镽edlock分布式鎖的實(shí)現(xiàn)完全基于普通分布式鎖。
普通分布式鎖
Redis普通分布式鎖這個(gè)大家基本上只了解,本文不打算過(guò)多的介紹,上一篇文章《Redlock:Redis分布式鎖最牛逼的實(shí)現(xiàn)》也講的很細(xì),并且也說(shuō)到了幾個(gè)重要的注意點(diǎn)。
所以直接show you the code,畢竟talk is cheap。
redisson版本
本次測(cè)試選擇redisson 2.14.1版本。
單機(jī)模式
源碼如下:
// 構(gòu)造redisson實(shí)現(xiàn)分布式鎖必要的Config Config config = new Config(); config.useSingleServer().setAddress("redis://172.29.1.180:5379").setPassword("a123456").setDatabase(0); // 構(gòu)造RedissonClient RedissonClient redissonClient = Redisson.create(config); // 設(shè)置鎖定資源名稱 RLock disLock = redissonClient.getLock("DISLOCK"); boolean isLock; try { //嘗試獲取分布式鎖 isLock = disLock.tryLock(500, 15000, TimeUnit.MILLISECONDS); if (isLock) { //TODO if get lock success, do something; Thread.sleep(15000); } } catch (Exception e) { } finally { // 無(wú)論如何, 最后都要解鎖 disLock.unlock(); }
通過(guò)代碼可知,經(jīng)過(guò)Redisson的封裝,實(shí)現(xiàn)Redis分布式鎖非常方便,我們?cè)倏匆幌翿edis中的value是啥,和前文分析一樣,hash結(jié)構(gòu),key就是資源名稱,field就是UUID+threadId,value就是重入值,在分布式鎖時(shí),這個(gè)值為1(Redisson還可以實(shí)現(xiàn)重入鎖,那么這個(gè)值就取決于重入次數(shù)了):
172.29.1.180:5379> hgetall DISLOCK 1) "01a6d806-d282-4715-9bec-f51b9aa98110:1" 2) "1"
哨兵模式
即sentinel模式,實(shí)現(xiàn)代碼和單機(jī)模式幾乎一樣,唯一的不同就是Config的構(gòu)造:
Config config = new Config(); config.useSentinelServers().addSentinelAddress( "redis://172.29.3.245:26378","redis://172.29.3.245:26379", "redis://172.29.3.245:26380") .setMasterName("mymaster") .setPassword("a123456").setDatabase(0);
集群模式
集群模式構(gòu)造Config如下:
Config config = new Config(); config.useClusterServers().addNodeAddress( "redis://172.29.3.245:6375","redis://172.29.3.245:6376", "redis://172.29.3.245:6377", "redis://172.29.3.245:6378","redis://172.29.3.245:6379", "redis://172.29.3.245:6380") .setPassword("a123456").setScanInterval(5000);
總結(jié)
普通分布式實(shí)現(xiàn)非常簡(jiǎn)單,無(wú)論是那種架構(gòu),向Redis通過(guò)EVAL命令執(zhí)行LUA腳本即可。
Redlock分布式鎖
那么Redlock分布式鎖如何實(shí)現(xiàn)呢?以單機(jī)模式Redis架構(gòu)為例,直接看實(shí)現(xiàn)代碼:
Config config1 = new Config(); config1.useSingleServer().setAddress("redis://172.29.1.180:5378") .setPassword("a123456").setDatabase(0); RedissonClient redissonClient1 = Redisson.create(config1); Config config2 = new Config(); config2.useSingleServer().setAddress("redis://172.29.1.180:5379") .setPassword("a123456").setDatabase(0); RedissonClient redissonClient2 = Redisson.create(config2); Config config3 = new Config(); config3.useSingleServer().setAddress("redis://172.29.1.180:5380") .setPassword("a123456").setDatabase(0); RedissonClient redissonClient3 = Redisson.create(config3); String resourceName = "REDLOCK"; RLock lock1 = redissonClient1.getLock(resourceName); RLock lock2 = redissonClient2.getLock(resourceName); RLock lock3 = redissonClient3.getLock(resourceName); RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3); boolean isLock; try { isLock = redLock.tryLock(500, 30000, TimeUnit.MILLISECONDS); System.out.println("isLock = "+isLock); if (isLock) { //TODO if get lock success, do something; Thread.sleep(30000); } } catch (Exception e) { } finally { // 無(wú)論如何, 最后都要解鎖 System.out.println(""); redLock.unlock(); }
最核心的變化就是RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);,因?yàn)槲疫@里是以三個(gè)節(jié)點(diǎn)為例。
那么如果是哨兵模式呢?需要搭建3個(gè),或者5個(gè)sentinel模式集群(具體多少個(gè),取決于你)。
那么如果是集群模式呢?需要搭建3個(gè),或者5個(gè)cluster模式集群(具體多少個(gè),取決于你)。
實(shí)現(xiàn)原理
既然核心變化是使用了RedissonRedLock,那么我們看一下它的源碼有什么不同。這個(gè)類是RedissonMultiLock的子類,所以調(diào)用tryLock方法時(shí),事實(shí)上調(diào)用了RedissonMultiLock的tryLock方法,精簡(jiǎn)源碼如下:
public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException { // 實(shí)現(xiàn)要點(diǎn)之允許加鎖失敗節(jié)點(diǎn)限制 int failedLocksLimit = failedLocksLimit(); List<RLock> acquiredLocks = new ArrayList<RLock>(locks.size()); // 實(shí)現(xiàn)要點(diǎn)之遍歷所有節(jié)點(diǎn)通過(guò)EVAL命令執(zhí)行l(wèi)ua加鎖 for (ListIterator<RLock> iterator = locks.listIterator(); iterator.hasNext();) { RLock lock = iterator.next(); boolean lockAcquired; try { // 對(duì)節(jié)點(diǎn)嘗試加鎖 lockAcquired = lock.tryLock(awaitTime, newLeaseTime, TimeUnit.MILLISECONDS); } catch (RedisConnectionClosedException|RedisResponseTimeoutException e) { // 如果拋出這類異常,為了防止加鎖成功,但是響應(yīng)失敗,需要解鎖 unlockInner(Arrays.asList(lock)); lockAcquired = false; } catch (Exception e) { // 拋出異常表示獲取鎖失敗 lockAcquired = false; } if (lockAcquired) { // 成功獲取鎖集合 acquiredLocks.add(lock); } else { // 如果達(dá)到了允許加鎖失敗節(jié)點(diǎn)限制,那么break,即此次Redlock加鎖失敗 if (locks.size() - acquiredLocks.size() == failedLocksLimit()) { break; } } } return true; }
很明顯,這段源碼就是上一篇文章《Redlock:Redis分布式鎖最牛逼的實(shí)現(xiàn)》提到的Redlock算法的完全實(shí)現(xiàn)。
以sentinel模式架構(gòu)為例,如下圖所示,有sentinel-1,sentinel-2,sentinel-3總計(jì)3個(gè)sentinel模式集群,如果要獲取分布式鎖,那么需要向這3個(gè)sentinel集群通過(guò)EVAL命令執(zhí)行LUA腳本,需要3/2+1=2,即至少2個(gè)sentinel集群響應(yīng)成功,才算成功的以Redlock算法獲取到分布式鎖:
問(wèn)題合集
根據(jù)上面實(shí)現(xiàn)原理的分析,這位同學(xué)應(yīng)該是對(duì)Redlock算法實(shí)現(xiàn)有一點(diǎn)點(diǎn)誤解,假設(shè)我們用5個(gè)節(jié)點(diǎn)實(shí)現(xiàn)Redlock算法的分布式鎖。那么要么是5個(gè)redis單實(shí)例,要么是5個(gè)sentinel集群,要么是5個(gè)cluster集群。而不是一個(gè)有5個(gè)主節(jié)點(diǎn)的cluster集群,然后向每個(gè)節(jié)點(diǎn)通過(guò)EVAL命令執(zhí)行LUA腳本嘗試獲取分布式鎖,如上圖所示。
失效時(shí)間如何設(shè)置
這個(gè)問(wèn)題的場(chǎng)景是,假設(shè)設(shè)置失效時(shí)間10秒,如果由于某些原因?qū)е?0秒還沒(méi)執(zhí)行完任務(wù),這時(shí)候鎖自動(dòng)失效,導(dǎo)致其他線程也會(huì)拿到分布式鎖。
這確實(shí)是Redis分布式最大的問(wèn)題,不管是普通分布式鎖,還是Redlock算法分布式鎖,都沒(méi)有解決這個(gè)問(wèn)題。也有一些文章提出了對(duì)失效時(shí)間續(xù)租,即延長(zhǎng)失效時(shí)間,很明顯這又提升了分布式鎖的復(fù)雜度。另外就筆者了解,沒(méi)有現(xiàn)成的框架有實(shí)現(xiàn),如果有哪位知道,可以告訴我,萬(wàn)分感謝。
redis分布式鎖的高可用
關(guān)于Redis分布式鎖的安全性問(wèn)題,在分布式系統(tǒng)專家Martin Kleppmann和Redis的作者antirez之間已經(jīng)發(fā)生過(guò)一場(chǎng)爭(zhēng)論。有興趣的同學(xué),搜索"基于Redis的分布式鎖到底安全嗎"就能得到你想要的答案,需要注意的是,有上下兩篇(這應(yīng)該就是傳說(shuō)中的神仙打架吧,哈)。
zookeeper or redis
沒(méi)有絕對(duì)的好壞,只有更適合自己的業(yè)務(wù)。就性能而言,redis很明顯優(yōu)于zookeeper;就分布式鎖實(shí)現(xiàn)的健壯性而言,zookeeper很明顯優(yōu)于redis。如何選擇,取決于你的業(yè)務(wù)!
到此這篇關(guān)于Redisson實(shí)現(xiàn)Redis分布式鎖的幾種方式的文章就介紹到這了,更多相關(guān)Redisson實(shí)現(xiàn)Redis分布式鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解Centos7下配置Redis并開機(jī)自啟動(dòng)
本篇文章主要介紹了Centos7下配置Redis并開機(jī)自啟動(dòng),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2016-11-11Windows系統(tǒng)安裝redis數(shù)據(jù)庫(kù)
這篇文章介紹了Windows系統(tǒng)安裝redis數(shù)據(jù)庫(kù)的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03odoo中使用redis實(shí)現(xiàn)緩存的步驟
這篇文章主要介紹了odoo中使用redis實(shí)現(xiàn)緩存的步驟,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-04-04Redis 出現(xiàn)錯(cuò)誤1067的解決辦法
這篇文章主要介紹了Redis 出現(xiàn)錯(cuò)誤1067的解決辦法的相關(guān)資料,Redis 錯(cuò)誤1067:進(jìn)程意外終止,Redis不能啟動(dòng),Redis啟動(dòng)不了,需要的朋友可以參考下2017-07-07Redis 操作多個(gè)數(shù)據(jù)庫(kù)的配置的方法實(shí)現(xiàn)
本文主要介紹了Redis 操作多個(gè)數(shù)據(jù)庫(kù)的配置的方法實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-035分鐘教你docker安裝啟動(dòng)redis全教程(全新方式)
今天,我來(lái)帶大家使用一種全新的方式docker來(lái)安裝redis,首先我們來(lái)了解一下什么是redis以及我們?yōu)槭裁匆褂胷edis,以及它的優(yōu)缺點(diǎn),感興趣的朋友跟隨小編一起學(xué)習(xí)下吧2021-05-05