Redisson實現(xiàn)Redis分布式鎖的N種姿勢

前幾天發(fā)的一篇文章《Redlock:Redis分布式鎖最牛逼的實現(xiàn)》飞蚓,引起了一些同學(xué)的討論蛤虐,也有一些同學(xué)提出了一些疑問,這是好事兒即彪。本文在講解如何使用Redisson實現(xiàn)Redis普通分布式鎖味抖,以及Redlock算法分布式鎖的幾種方式的同時评甜,也附帶解答這些同學(xué)的一些疑問。

Redis幾種架構(gòu)

Redis發(fā)展到現(xiàn)在仔涩,幾種常見的部署架構(gòu)有:

  1. 單機模式忍坷;
  2. 主從模式;
  3. 哨兵模式熔脂;
  4. 集群模式佩研;

我們首先基于這些架構(gòu)講解Redisson普通分布式鎖實現(xiàn),需要注意的是霞揉,只有充分了解普通分布式鎖是如何實現(xiàn)的旬薯,才能更好的了解Redlock分布式鎖的實現(xiàn),因為Redlock分布式鎖的實現(xiàn)完全基于普通分布式鎖适秩。

普通分布式鎖

Redis普通分布式鎖這個大家基本上只了解绊序,本文不打算過多的介紹,上一篇文章《Redlock:Redis分布式鎖最牛逼的實現(xiàn)》也講的很細秽荞,并且也說到了幾個重要的注意點政模。

所以直接show you the code,畢竟talk is cheap蚂会。

  • redisson版本

本次測試選擇redisson 2.14.1版本。

單機模式

源碼如下:

// 構(gòu)造redisson實現(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 {
    // 無論如何, 最后都要解鎖
    disLock.unlock();
}

通過代碼可知耗式,經(jīng)過Redisson的封裝胁住,實現(xiàn)Redis分布式鎖非常方便,我們再看一下Redis中的value是啥刊咳,和前文分析一樣彪见,hash結(jié)構(gòu),key就是資源名稱娱挨,field就是UUID+threadId余指,value就是重入值,在分布式鎖時,這個值為1(Redisson還可以實現(xiàn)重入鎖酵镜,那么這個值就取決于重入次數(shù)了):

172.29.1.180:5379> hgetall DISLOCK
1) "01a6d806-d282-4715-9bec-f51b9aa98110:1"
2) "1"

哨兵模式

即sentinel模式碉碉,實現(xiàn)代碼和單機模式幾乎一樣,唯一的不同就是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é)

普通分布式實現(xiàn)非常簡單淮韭,無論是那種架構(gòu)垢粮,向Redis通過EVAL命令執(zhí)行LUA腳本即可。

Redlock分布式鎖

那么Redlock分布式鎖如何實現(xiàn)呢靠粪?以單機模式Redis架構(gòu)為例蜡吧,直接看實現(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 {
    // 無論如何, 最后都要解鎖
    System.out.println("");
    redLock.unlock();
}

最核心的變化就是RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);,因為我這里是以三個節(jié)點為例占键。

那么如果是哨兵模式呢昔善?需要搭建3個,或者5個sentinel模式集群(具體多少個畔乙,取決于你)君仆。
那么如果是集群模式呢?需要搭建3個啸澡,或者5個cluster模式集群(具體多少個袖订,取決于你)。

實現(xiàn)原理

既然核心變化是使用了RedissonRedLock嗅虏,那么我們看一下它的源碼有什么不同洛姑。這個類是RedissonMultiLock的子類,所以調(diào)用tryLock方法時皮服,事實上調(diào)用了RedissonMultiLock的tryLock方法楞艾,精簡源碼如下:

public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
    // 實現(xiàn)要點之允許加鎖失敗節(jié)點限制
    int failedLocksLimit = failedLocksLimit();
    List<RLock> acquiredLocks = new ArrayList<RLock>(locks.size());
    // 實現(xiàn)要點之遍歷所有節(jié)點通過EVAL命令執(zhí)行l(wèi)ua加鎖
    for (ListIterator<RLock> iterator = locks.listIterator(); iterator.hasNext();) {
        RLock lock = iterator.next();
        boolean lockAcquired;
        try {
            // 對節(jié)點嘗試加鎖
            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 {
            // 如果達到了允許加鎖失敗節(jié)點限制,那么break择同,即此次Redlock加鎖失敗
            if (locks.size() - acquiredLocks.size() == failedLocksLimit()) {
                break;
            }               
        }
    }
    return true;
}

很明顯两入,這段源碼就是上一篇文章《Redlock:Redis分布式鎖最牛逼的實現(xiàn)》提到的Redlock算法的完全實現(xiàn)。

以sentinel模式架構(gòu)為例敲才,如下圖所示裹纳,有sentinel-1,sentinel-2紧武,sentinel-3總計3個sentinel模式集群剃氧,如果要獲取分布式鎖,那么需要向這3個sentinel集群通過EVAL命令執(zhí)行LUA腳本阻星,需要3/2+1=2朋鞍,即至少2個sentinel集群響應(yīng)成功,才算成功的以Redlock算法獲取到分布式鎖:

Redlock分布式鎖

問題合集

image.png

根據(jù)上面實現(xiàn)原理的分析,這位同學(xué)應(yīng)該是對Redlock算法實現(xiàn)有一點點誤解滥酥,假設(shè)我們用5個節(jié)點實現(xiàn)Redlock算法的分布式鎖更舞。那么要么是5個redis單實例,要么是5個sentinel集群恨狈,要么是5個cluster集群疏哗。而不是一個有5個主節(jié)點的cluster集群,然后向每個節(jié)點通過EVAL命令執(zhí)行LUA腳本嘗試獲取分布式鎖禾怠,如上圖所示返奉。

  • 失效時間如何設(shè)置

這個問題的場景是,假設(shè)設(shè)置失效時間10秒吗氏,如果由于某些原因?qū)е?0秒還沒執(zhí)行完任務(wù)芽偏,這時候鎖自動失效,導(dǎo)致其他線程也會拿到分布式鎖弦讽。

這確實是Redis分布式最大的問題污尉,不管是普通分布式鎖,還是Redlock算法分布式鎖往产,都沒有解決這個問題被碗。也有一些文章提出了對失效時間續(xù)租,即延長失效時間仿村,很明顯這又提升了分布式鎖的復(fù)雜度锐朴。另外就筆者了解,沒有現(xiàn)成的框架有實現(xiàn)蔼囊,如果有哪位知道焚志,可以告訴我,萬分感謝畏鼓。

  • redis分布式鎖的高可用

關(guān)于Redis分布式鎖的安全性問題酱酬,在分布式系統(tǒng)專家Martin Kleppmann和Redis的作者antirez之間已經(jīng)發(fā)生過一場爭論。有興趣的同學(xué)云矫,搜索"基于Redis的分布式鎖到底安全嗎"就能得到你想要的答案膳沽,需要注意的是,有上下兩篇(這應(yīng)該就是傳說中的神仙打架吧让禀,哈)贵少。

  • zookeeper or redis

沒有絕對的好壞,只有更適合自己的業(yè)務(wù)堆缘。就性能而言,redis很明顯優(yōu)于zookeeper普碎;就分布式鎖實現(xiàn)的健壯性而言吼肥,zookeeper很明顯優(yōu)于redis。如何選擇,取決于你的業(yè)務(wù)缀皱!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末斗这,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子啤斗,更是在濱河造成了極大的恐慌表箭,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钮莲,死亡現(xiàn)場離奇詭異免钻,居然都是意外死亡,警方通過查閱死者的電腦和手機崔拥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門极舔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人链瓦,你說我怎么就攤上這事拆魏。” “怎么了慈俯?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵渤刃,是天一觀的道長。 經(jīng)常有香客問我贴膘,道長卖子,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任步鉴,我火速辦了婚禮揪胃,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘氛琢。我一直安慰自己喊递,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布阳似。 她就那樣靜靜地躺著骚勘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪撮奏。 梳的紋絲不亂的頭發(fā)上俏讹,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天,我揣著相機與錄音,去河邊找鬼纱昧。 笑死榜晦,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的殉疼。 我是一名探鬼主播梯浪,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼瓢娜!你這毒婦竟也來了挂洛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤眠砾,失蹤者是張志新(化名)和其女友劉穎虏劲,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體褒颈,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡柒巫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了哈肖。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吻育。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖淤井,靈堂內(nèi)的尸體忽然破棺而出布疼,到底是詐尸還是另有隱情,我是刑警寧澤币狠,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布游两,位于F島的核電站,受9級特大地震影響漩绵,放射性物質(zhì)發(fā)生泄漏贱案。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一止吐、第九天 我趴在偏房一處隱蔽的房頂上張望宝踪。 院中可真熱鬧,春花似錦碍扔、人聲如沸瘩燥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽厉膀。三九已至,卻和暖如春二拐,著一層夾襖步出監(jiān)牢的瞬間服鹅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工百新, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留企软,地道東北人。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓饭望,卻偏偏與公主長得像澜倦,于是被迫代替她去往敵國和親聚蝶。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,976評論 2 355