使用Zookeeper實(shí)現(xiàn)分布式鎖
如何用Zookeeper實(shí)現(xiàn)分布式鎖?
在學(xué)習(xí)分布式鎖之前,需要首先了解一下Zookeeper的[臨時(shí)順序節(jié)點(diǎn)]。
什么是臨時(shí)順序節(jié)點(diǎn)?
讓我們來回顧一下Zookeeper節(jié)點(diǎn)的概念:

Zookeeper的數(shù)據(jù)存儲(chǔ)結(jié)構(gòu)就像一棵樹,這棵樹由節(jié)點(diǎn)組成,這種節(jié)點(diǎn)叫做Znode。
Znode分為四種類型
1.持久節(jié)點(diǎn) (PERSISTENT)
默認(rèn)的節(jié)點(diǎn)類型。創(chuàng)建節(jié)點(diǎn)的客戶端與zookeeper斷開連接后,該節(jié)點(diǎn)依舊存在 。
2.持久節(jié)點(diǎn)順序節(jié)點(diǎn)(PERSISTENT_SEQUENTIAL)
所謂順序節(jié)點(diǎn),就是在創(chuàng)建節(jié)點(diǎn)時(shí),Zookeeper根據(jù)創(chuàng)建的時(shí)間順序給該節(jié)點(diǎn)名稱進(jìn)行編號(hào):

3.臨時(shí)節(jié)點(diǎn)(EPHEMERAL)
和持久節(jié)點(diǎn)相反,當(dāng)創(chuàng)建節(jié)點(diǎn)的客戶端與zookeeper斷開連接后,臨時(shí)節(jié)點(diǎn)會(huì)被刪除。



4.臨時(shí)順序節(jié)點(diǎn)(EPHEMERAL_SEQUENTIAL)
顧名思義,臨時(shí)順序節(jié)點(diǎn)結(jié)合和臨時(shí)節(jié)點(diǎn)和順序節(jié)點(diǎn)的特點(diǎn):在創(chuàng)建節(jié)點(diǎn)時(shí),Zookeeper根據(jù)創(chuàng)建的時(shí)間順序給該節(jié)點(diǎn)名稱進(jìn)行編號(hào);當(dāng)創(chuàng)建節(jié)點(diǎn)的客戶端與Zookeeper斷開連接后,臨時(shí)節(jié)點(diǎn)會(huì)被刪除。
那臨時(shí)順序節(jié)點(diǎn)和Zookeeper的分布式鎖有什么關(guān)系呢?
Zookeeper分布式鎖的原理
Zookeeper分布式鎖恰恰應(yīng)用了臨時(shí)順序節(jié)點(diǎn)。具體如何實(shí)現(xiàn)呢?讓我們來看一看詳細(xì)步驟:
獲取鎖
首先,在Zookeeper當(dāng)中創(chuàng)建一個(gè)持久節(jié)點(diǎn)ParentLock。當(dāng)?shù)谝粋€(gè)客戶端想要獲得鎖時(shí),需要在ParentLock這個(gè)節(jié)點(diǎn)下面創(chuàng)建一個(gè)臨時(shí)順序節(jié)點(diǎn) Lock1。

之后,Client1查找ParentLock下面所有的臨時(shí)順序節(jié)點(diǎn)并排序,判斷自己所創(chuàng)建的節(jié)點(diǎn)Lock1是不是順序最靠前的一個(gè)。如果是第一個(gè)節(jié)點(diǎn),則成功獲得鎖。

這時(shí)候,如果再有一個(gè)客戶端 Client2 前來獲取鎖,則在ParentLock下載再創(chuàng)建一個(gè)臨時(shí)順序節(jié)點(diǎn)Lock2。

Client2查找ParentLock下面所有的臨時(shí)順序節(jié)點(diǎn)并排序,判斷自己所創(chuàng)建的節(jié)點(diǎn)Lock2是不是順序最靠前的一個(gè),結(jié)果發(fā)現(xiàn)節(jié)點(diǎn)Lock2并不是最小的。
于是,Client2向排序僅比它靠前的節(jié)點(diǎn)Lock1注冊(cè)Watcher,用于監(jiān)聽Lock1節(jié)點(diǎn)是否存在。這意味著Client2搶鎖失敗,進(jìn)入了等待狀態(tài)。

這時(shí)候,如果又有一個(gè)客戶端Client3前來獲取鎖,則在ParentLock下載再創(chuàng)建一個(gè)臨時(shí)順序節(jié)點(diǎn)Lock3。

Client3查找ParentLock下面所有的臨時(shí)順序節(jié)點(diǎn)并排序,判斷自己所創(chuàng)建的節(jié)點(diǎn)Lock3是不是順序最靠前的一個(gè),結(jié)果同樣發(fā)現(xiàn)節(jié)點(diǎn)Lock3并不是最小的。
于是,Client3向排序僅比它靠前的節(jié)點(diǎn)Lock2注冊(cè)Watcher,用于監(jiān)聽Lock2節(jié)點(diǎn)是否存在。這意味著Client3同樣搶鎖失敗,進(jìn)入了等待狀態(tài)。

這樣一來,Client1得到了鎖,Client2監(jiān)聽了Lock1,Client3監(jiān)聽了Lock2。這恰恰形成了一個(gè)等待隊(duì)列,很像是Java當(dāng)中ReentrantLock所依賴的AQS(AbstractQueuedSynchronizer)。
獲得鎖的過程大致就是這樣,那么Zookeeper如何釋放鎖呢?
釋放鎖的過程很簡單,只需要釋放對(duì)應(yīng)的子節(jié)點(diǎn)就好。
釋放鎖
釋放鎖分為兩種情況:
1.任務(wù)完成,客戶端顯示釋放
當(dāng)任務(wù)完成時(shí),Client1會(huì)顯示調(diào)用刪除節(jié)點(diǎn)Lock1的指令。

2.任務(wù)執(zhí)行過程中,客戶端崩潰
獲得鎖的Client1在任務(wù)執(zhí)行過程中,如果Duang的一聲崩潰,則會(huì)斷開與Zookeeper服務(wù)端的鏈接。根據(jù)臨時(shí)節(jié)點(diǎn)的特性,相關(guān)聯(lián)的節(jié)點(diǎn)Lock1會(huì)隨之自動(dòng)刪除。

由于Client2一直監(jiān)聽著Lock1的存在狀態(tài),當(dāng)Lock1節(jié)點(diǎn)被刪除,Client2會(huì)立刻收到通知。這時(shí)候Client2會(huì)再次查詢ParentLock下面的所有節(jié)點(diǎn),確認(rèn)自己創(chuàng)建的節(jié)點(diǎn)Lock2是不是目前最小的節(jié)點(diǎn)。如果是最小,則Client2順理成章獲得了鎖。

同理,如果Client2也因?yàn)槿蝿?wù)完成或者節(jié)點(diǎn)崩潰而刪除了節(jié)點(diǎn)Lock2,那么Client3就會(huì)接到通知。

最終,Client3成功得到了鎖。

使用Zookeeper實(shí)現(xiàn)分布式鎖的大致流程就是這樣。那么使用Zookeeper實(shí)現(xiàn)的分布式鎖和Redis實(shí)現(xiàn)的分布式鎖相比有什么優(yōu)勢和劣勢呢?
下面總結(jié)一下他們各自的優(yōu)劣
| 分布式鎖 | 優(yōu)點(diǎn) | 缺點(diǎn) |
| Zookeeper | 1.有封裝好的框架,容易實(shí)現(xiàn) 2.有等待鎖的隊(duì)列,大大提升搶鎖效率。 | 添加和刪除節(jié)點(diǎn)性能較低 |
| Redis | Set和Del指令性能較高 | 1.實(shí)現(xiàn)復(fù)雜,需要考慮超時(shí),原子性,誤刪等情形。 2.沒有等待鎖的隊(duì)列,只能在客戶端自旋來等待,效率低下。 |
有人說Zookeeper實(shí)現(xiàn)的分布式鎖支持可重入,Redis實(shí)現(xiàn)的分布式鎖不支持可重入,這是錯(cuò)誤的觀點(diǎn)。兩者都可以在客戶端實(shí)現(xiàn)可重入邏輯。
關(guān)于Zookeeper分布式鎖的內(nèi)容就介紹到這里啦。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
如何在Redis中實(shí)現(xiàn)分頁排序查詢過程解析
這篇文章主要介紹了如何在Redis中實(shí)現(xiàn)分頁排序查詢過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07
SpringBoot整合Hutool實(shí)現(xiàn)文件上傳的使用示例
文件上傳在項(xiàng)目經(jīng)常會(huì)用到,本文主要介紹了SpringBoot整合Hutool實(shí)現(xiàn)文件上傳的使用示例,具有一定的參考價(jià)值,感興趣的可以了解一下2023-11-11
IDEA使用properties配置文件進(jìn)行mysql數(shù)據(jù)庫連接的教程圖解
Properties類是 鍵和值均為字符串的可以永久存儲(chǔ)到文件中的key-value集合。這篇文章主要介紹了IDEA使用properties配置文件進(jìn)行mysql數(shù)據(jù)路連接 ,需要的朋友可以參考下2018-10-10
SpringBoot實(shí)現(xiàn)單文件與多文件上傳
本次例子不基于第三方存儲(chǔ)(如七牛云對(duì)象存儲(chǔ)、阿里云對(duì)象存儲(chǔ)、騰訊云對(duì)象存儲(chǔ)等),僅基于本地存儲(chǔ)。本文主要內(nèi)容如下:公共文件存儲(chǔ)代碼;單文件上傳代碼;多文件上傳代碼2021-05-05
Java報(bào)錯(cuò)Non-terminating?decimal?expansion解決分析
這篇文章主要為大家介紹了Java報(bào)錯(cuò)Non-terminating?decimal?expansion解決方案及原理分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09
劍指Offer之Java算法習(xí)題精講數(shù)組與列表的查找及字符串轉(zhuǎn)換
跟著思路走,之后從簡單題入手,反復(fù)去看,做過之后可能會(huì)忘記,之后再做一次,記不住就反復(fù)做,反復(fù)尋求思路和規(guī)律,慢慢積累就會(huì)發(fā)現(xiàn)質(zhì)的變化2022-03-03

