ZooKeeper分布式鎖的實(shí)現(xiàn)原理(轉(zhuǎn)載)

文章轉(zhuǎn)載自:https://juejin.im/post/5c01532ef265da61362232ed#comment

一、寫在前面

之前寫過一篇文章(《拜托,面試請不要再問我Redis分布式鎖的實(shí)現(xiàn)原理》),給大家說了一下Redisson這個開源框架是如何實(shí)現(xiàn)Redis分布式鎖原理的,這篇文章再給大家聊一下ZooKeeper實(shí)現(xiàn)分布式鎖的原理。

同理,我是直接基于比較常用的Curator這個開源框架,聊一下這個框架對ZooKeeper(以下簡稱zk)分布式鎖的實(shí)現(xiàn)。

一般除了大公司是自行封裝分布式鎖框架之外,建議大家用這些開源框架封裝好的分布式鎖實(shí)現(xiàn),這是一個比較快捷省事兒的方式。

二、ZooKeeper分布式鎖機(jī)制

接下來我們一起來看看,多客戶端獲取及釋放zk分布式鎖的整個流程及背后的原理。

首先大家看看下面的圖,如果現(xiàn)在有兩個客戶端一起要爭搶zk上的一把分布式鎖,會是個什么場景?

image.png

如果大家對zk還不太了解的話,建議先自行百度一下,簡單了解點(diǎn)基本概念,比如zk有哪些節(jié)點(diǎn)類型等等。

參見上圖。zk里有一把鎖,這個鎖就是zk上的一個節(jié)點(diǎn)。然后呢,兩個客戶端都要來獲取這個鎖,具體是怎么來獲取呢?

咱們就假設(shè)客戶端A搶先一步,對zk發(fā)起了加分布式鎖的請求,這個加鎖請求是用到了zk中的一個特殊的概念,叫做“臨時順序節(jié)點(diǎn)”。

簡單來說,就是直接在"my_lock"這個鎖節(jié)點(diǎn)下,創(chuàng)建一個順序節(jié)點(diǎn),這個順序節(jié)點(diǎn)有zk內(nèi)部自行維護(hù)的一個節(jié)點(diǎn)序號。

比如說,第一個客戶端來搞一個順序節(jié)點(diǎn),zk內(nèi)部會給起個名字叫做:xxx-000001。然后第二個客戶端來搞一個順序節(jié)點(diǎn),zk可能會起個名字叫做:xxx-000002。大家注意一下,最后一個數(shù)字都是依次遞增的,從1開始逐次遞增。zk會維護(hù)這個順序。

所以這個時候,假如說客戶端A先發(fā)起請求,就會搞出來一個順序節(jié)點(diǎn),大家看下面的圖,Curator框架大概會弄成如下的樣子:

image.png

大家看,客戶端A發(fā)起一個加鎖請求,先會在你要加鎖的node下搞一個臨時順序節(jié)點(diǎn),這一大坨長長的名字都是Curator框架自己生成出來的。

然后,那個最后一個數(shù)字是"1"。大家注意一下,因?yàn)榭蛻舳薃是第一個發(fā)起請求的,所以給他搞出來的順序節(jié)點(diǎn)的序號是"1"。

接著客戶端A創(chuàng)建完一個順序節(jié)點(diǎn)。還沒完,他會查一下"my_lock"這個鎖節(jié)點(diǎn)下的所有子節(jié)點(diǎn),并且這些子節(jié)點(diǎn)是按照序號排序的,這個時候他大概會拿到這么一個集合:

image.png

接著客戶端A會走一個關(guān)鍵性的判斷,就是說:唉!兄弟,這個集合里,我創(chuàng)建的那個順序節(jié)點(diǎn),是不是排在第一個???

如果是的話,那我就可以加鎖了??!因?yàn)槊髅魑揖褪堑谝粋€來創(chuàng)建順序節(jié)點(diǎn)的人,所以我就是第一個嘗試加分布式鎖的人?。?/p>

bingo!加鎖成功!大家看下面的圖,再來直觀的感受一下整個過程。

image.png

接著假如說,客戶端A都加完鎖了,客戶端B過來想要加鎖了,這個時候他會干一樣的事兒:先是在"my_lock"這個鎖節(jié)點(diǎn)下創(chuàng)建一個臨時順序節(jié)點(diǎn),此時名字會變成類似于:

image.png

大家看看下面的圖:

image.png

客戶端B因?yàn)槭堑诙€來創(chuàng)建順序節(jié)點(diǎn)的,所以zk內(nèi)部會維護(hù)序號為"2"。

接著客戶端B會走加鎖判斷邏輯,查詢"my_lock"鎖節(jié)點(diǎn)下的所有子節(jié)點(diǎn),按序號順序排列,此時他看到的類似于:

image.png

同時檢查自己創(chuàng)建的順序節(jié)點(diǎn),是不是集合中的第一個?

明顯不是啊,此時第一個是客戶端A創(chuàng)建的那個順序節(jié)點(diǎn),序號為"01"的那個。所以加鎖失敗

加鎖失敗了以后,客戶端B就會通過ZK的API對他的順序節(jié)點(diǎn)的上一個順序節(jié)點(diǎn)加一個監(jiān)聽器。zk天然就可以實(shí)現(xiàn)對某個節(jié)點(diǎn)的監(jiān)聽。

如果大家還不知道zk的基本用法,可以百度查閱,非常的簡單??蛻舳薆的順序節(jié)點(diǎn)是:

image.png

他的上一個順序節(jié)點(diǎn),不就是下面這個嗎?

image.png

即客戶端A創(chuàng)建的那個順序節(jié)點(diǎn)!

所以,客戶端B會對:

image.png

這個節(jié)點(diǎn)加一個監(jiān)聽器,監(jiān)聽這個節(jié)點(diǎn)是否被刪除等變化!大家看下面的圖。

image.png

接著,客戶端A加鎖之后,可能處理了一些代碼邏輯,然后就會釋放鎖。那么,釋放鎖是個什么過程呢?

其實(shí)很簡單,就是把自己在zk里創(chuàng)建的那個順序節(jié)點(diǎn),也就是:

image.png

這個節(jié)點(diǎn)給刪除。

刪除了那個節(jié)點(diǎn)之后,zk會負(fù)責(zé)通知監(jiān)聽這個節(jié)點(diǎn)的監(jiān)聽器,也就是客戶端B之前加的那個監(jiān)聽器,說:兄弟,你監(jiān)聽的那個節(jié)點(diǎn)被刪除了,有人釋放了鎖。

image.png

此時客戶端B的監(jiān)聽器感知到了上一個順序節(jié)點(diǎn)被刪除,也就是排在他之前的某個客戶端釋放了鎖。

此時,就會通知客戶端B重新嘗試去獲取鎖,也就是獲取"my_lock"節(jié)點(diǎn)下的子節(jié)點(diǎn)集合,此時為:

image.png

集合里此時只有客戶端B創(chuàng)建的唯一的一個順序節(jié)點(diǎn)了!

然后呢,客戶端B判斷自己居然是集合中的第一個順序節(jié)點(diǎn),bingo!可以加鎖了!直接完成加鎖,運(yùn)行后續(xù)的業(yè)務(wù)代碼即可,運(yùn)行完了之后再次釋放鎖。

image.png

三、總結(jié)

其實(shí)如果有客戶端C、客戶端D等N個客戶端爭搶一個zk分布式鎖,原理都是類似的。

  • 大家都是上來直接創(chuàng)建一個鎖節(jié)點(diǎn)下的一個接一個的臨時順序節(jié)點(diǎn)
  • 如果自己不是第一個節(jié)點(diǎn),就對自己上一個節(jié)點(diǎn)加監(jiān)聽器
  • 只要上一個節(jié)點(diǎn)釋放鎖,自己就排到前面去了,相當(dāng)于是一個排隊(duì)機(jī)制。

而且用臨時順序節(jié)點(diǎn)的另外一個用意就是,如果某個客戶端創(chuàng)建臨時順序節(jié)點(diǎn)之后,不小心自己宕機(jī)了也沒關(guān)系,zk感知到那個客戶端宕機(jī),會自動刪除對應(yīng)的臨時順序節(jié)點(diǎn),相當(dāng)于自動釋放鎖,或者是自動取消自己的排隊(duì)。

最后,咱們來看下用Curator框架進(jìn)行加鎖和釋放鎖的一個過程:

image.png

其實(shí)用開源框架就是這點(diǎn)好,方便。這個Curator框架的zk分布式鎖的加鎖和釋放鎖的實(shí)現(xiàn)原理,其實(shí)就是上面我們說的那樣子。

但是如果你要手動實(shí)現(xiàn)一套那個代碼的話。還是有點(diǎn)麻煩的,要考慮到各種細(xì)節(jié),異常處理等等。所以大家如果考慮用zk分布式鎖,可以參考下本文的思路。

END

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容