Zookeeper實現(xiàn)分布式鎖

如何使用zookeeper實現(xiàn)分布式鎖,在描述算法流程之前,先看下zookeeper中幾個關(guān)于節(jié)點的有趣的性質(zhì):

有序節(jié)點:假如當(dāng)前有一個父節(jié)點為/lock,我們可以在這個父節(jié)點下面創(chuàng)建子節(jié)點趁蕊;zookeeper提供了一個可選的有序特性缀棍,例如我們可以創(chuàng)建子節(jié)點“/lock/node-”并且指明有序,那么zookeeper在生成子節(jié)點時會根據(jù)當(dāng)前的子節(jié)點數(shù)量自動添加整數(shù)序號捌肴,也就是說如果是第一個創(chuàng)建的子節(jié)點枪眉,那么生成的子節(jié)點為/lock/node-0000000000捺檬,下一個節(jié)點則為/lock/node-0000000001,依次類推贸铜。
臨時節(jié)點:客戶端可以建立一個臨時節(jié)點堡纬,在會話結(jié)束或者會話超時后聂受,zookeeper會自動刪除該節(jié)點。
事件監(jiān)聽:在讀取數(shù)據(jù)時烤镐,我們可以同時對節(jié)點設(shè)置事件監(jiān)聽蛋济,當(dāng)節(jié)點數(shù)據(jù)或結(jié)構(gòu)變化時,zookeeper會通知客戶端炮叶。當(dāng)前zookeeper有如下四種事件:1)節(jié)點創(chuàng)建碗旅;2)節(jié)點刪除;3)節(jié)點數(shù)據(jù)修改镜悉;4)子節(jié)點變更祟辟。
下面描述使用zookeeper實現(xiàn)分布式鎖的算法流程,假設(shè)鎖空間的根節(jié)點為/lock:

客戶端連接zookeeper侣肄,并在/lock下創(chuàng)建臨時的且有序的子節(jié)點川尖,第一個客戶端對應(yīng)的子節(jié)點為/lock/lock-0000000000,第二個為/lock/lock-0000000001茫孔,以此類推。
客戶端獲取/lock下的子節(jié)點列表被芳,判斷自己創(chuàng)建的子節(jié)點是否為當(dāng)前子節(jié)點列表中序號最小的子節(jié)點缰贝,如果是則認(rèn)為獲得鎖,否則監(jiān)聽/lock的子節(jié)點變更消息畔濒,獲得子節(jié)點變更通知后重復(fù)此步驟直至獲得鎖剩晴;
執(zhí)行業(yè)務(wù)代碼;
完成業(yè)務(wù)流程后侵状,刪除對應(yīng)的子節(jié)點釋放鎖赞弥。
步驟1中創(chuàng)建的臨時節(jié)點能夠保證在故障的情況下鎖也能被釋放,考慮這么個場景:假如客戶端a當(dāng)前創(chuàng)建的子節(jié)點為序號最小的節(jié)點趣兄,獲得鎖之后客戶端所在機器宕機了绽左,客戶端沒有主動刪除子節(jié)點;如果創(chuàng)建的是永久的節(jié)點艇潭,那么這個鎖永遠不會釋放拼窥,導(dǎo)致死鎖;由于創(chuàng)建的是臨時節(jié)點蹋凝,客戶端宕機后鲁纠,過了一定時間zookeeper沒有收到客戶端的心跳包判斷會話失效,將臨時節(jié)點刪除從而釋放鎖鳍寂。

另外細(xì)心的朋友可能會想到改含,在步驟2中獲取子節(jié)點列表與設(shè)置監(jiān)聽這兩步操作的原子性問題,考慮這么個場景:客戶端a對應(yīng)子節(jié)點為/lock/lock-0000000000迄汛,客戶端b對應(yīng)子節(jié)點為/lock/lock-0000000001捍壤,客戶端b獲取子節(jié)點列表時發(fā)現(xiàn)自己不是序號最小的骤视,但是在設(shè)置監(jiān)聽器前客戶端a完成業(yè)務(wù)流程刪除了子節(jié)點/lock/lock-0000000000,客戶端b設(shè)置的監(jiān)聽器豈不是丟失了這個事件從而導(dǎo)致永遠等待了白群?這個問題不存在的尚胞。因為zookeeper提供的API中設(shè)置監(jiān)聽器的操作與讀操作是原子執(zhí)行的,也就是說在讀子節(jié)點列表時同時設(shè)置監(jiān)聽器帜慢,保證不會丟失事件笼裳。

最后,對于這個算法有個極大的優(yōu)化點:假如當(dāng)前有1000個節(jié)點在等待鎖粱玲,如果獲得鎖的客戶端釋放鎖時躬柬,這1000個客戶端都會被喚醒,這種情況稱為“羊群效應(yīng)”抽减;在這種羊群效應(yīng)中允青,zookeeper需要通知1000個客戶端,這會阻塞其他的操作卵沉,最好的情況應(yīng)該只喚醒新的最小節(jié)點對應(yīng)的客戶端颠锉。應(yīng)該怎么做呢?在設(shè)置事件監(jiān)聽時史汗,每個客戶端應(yīng)該對剛好在它之前的子節(jié)點設(shè)置事件監(jiān)聽琼掠,例如子節(jié)點列表為/lock/lock-0000000000、/lock/lock-0000000001停撞、/lock/lock-0000000002瓷蛙,序號為1的客戶端監(jiān)聽序號為0的子節(jié)點刪除消息,序號為2的監(jiān)聽序號為1的子節(jié)點刪除消息。

所以調(diào)整后的分布式鎖算法流程如下:

客戶端連接zookeeper,并在/lock下創(chuàng)建臨時的且有序的子節(jié)點愕宋,第一個客戶端對應(yīng)的子節(jié)點為/lock/lock-0000000000,第二個為/lock/lock-0000000001冠桃,以此類推。
客戶端獲取/lock下的子節(jié)點列表恐疲,判斷自己創(chuàng)建的子節(jié)點是否為當(dāng)前子節(jié)點列表中序號最小的子節(jié)點腊满,如果是則認(rèn)為獲得鎖,否則監(jiān)聽剛好在自己之前一位的子節(jié)點刪除消息培己,獲得子節(jié)點變更通知后重復(fù)此步驟直至獲得鎖碳蛋;
執(zhí)行業(yè)務(wù)代碼;
完成業(yè)務(wù)流程后省咨,刪除對應(yīng)的子節(jié)點釋放鎖肃弟。
接下來我們來看看在代碼中如何實現(xiàn)

package com.zdnst.boot.zk;

import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;

/**
 * Created by apple on 2019/3/27.
 */
public class ZKClientDemo {

    public static void main(String[] args){
        String connection = "127.0.0.1:2181";
        String LOCK_PATH = "/lock";
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000,3);
        CuratorFramework client = CuratorFrameworkFactory.newClient(connection, 3000, 3000,retryPolicy);
        client.start();
        InterProcessMutex mutex = new InterProcessMutex(client,LOCK_PATH);
        try {
            mutex.acquire();
            Thread.sleep(3000);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                mutex.release();
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                client.close();
            }
        }

    }


}

從代碼來看實現(xiàn)一個分布式鎖原來如此簡單。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市箩兽,隨后出現(xiàn)的幾起案子汗贫,更是在濱河造成了極大的恐慌,老刑警劉巖落包,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咐蝇,死亡現(xiàn)場離奇詭異有序,居然都是意外死亡,警方通過查閱死者的電腦和手機旭寿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進店門许师,熙熙樓的掌柜王于貴愁眉苦臉地迎上來微渠,“玉大人咧擂,你說我怎么就攤上這事∷缮辏” “怎么了?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵舅逸,是天一觀的道長皇筛。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么彪置? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任蝇恶,我火速辦了婚禮,結(jié)果婚禮上潘懊,老公的妹妹穿的比我還像新娘想虎。我一直安慰自己,他們只是感情好岂却,可當(dāng)我...
    茶點故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布躏哩。 她就那樣靜靜地躺著揉燃,像睡著了一般炊汤。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上姑曙,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天迈倍,我揣著相機與錄音,去河邊找鬼宴合。 笑死,一個胖子當(dāng)著我的面吹牛迹鹅,可吹牛的內(nèi)容都是我干的卦洽。 我是一名探鬼主播,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼逐样,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了脂新?” 一聲冷哼從身側(cè)響起挪捕,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎争便,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體滞乙,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡奏纪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了斩启。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片序调。...
    茶點故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡兔簇,死狀恐怖发绢,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤狸窘,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布翻擒,位于F島的核電站陋气,受9級特大地震影響春哨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望燃观。 院中可真熱鬧褒脯,春花似錦、人聲如沸缆毁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至颁督,卻和暖如春践啄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背沉御。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工屿讽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人吠裆。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓伐谈,卻偏偏與公主長得像,于是被迫代替她去往敵國和親试疙。 傳聞我的和親對象是個殘疾皇子诵棵,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,500評論 2 359