基于 Zookeeper 的分布式锁实现
1. 背景
最近在學(xué)習(xí) Zookeeper,在剛開(kāi)始接觸 Zookeeper 的時(shí)候,完全不知道 Zookeeper 有什么用。且很多資料都是將 Zookeeper 描述成一個(gè)“類 Unix/Linux 文件系統(tǒng)”的中間件,導(dǎo)致我很難將類 Unix/Linux 文件系統(tǒng)的 Zookeeper 和分布式應(yīng)用聯(lián)系在一起。后來(lái)在粗讀了《ZooKeeper 分布式過(guò)程協(xié)同技術(shù)詳解》和《從Paxos到Zookeeper 分布式一致性原理與實(shí)踐》兩本書(shū),并動(dòng)手寫(xiě)了一些 CURD demo 后,初步對(duì) Zookeeper 有了一定的了解。不過(guò)比較膚淺,為了進(jìn)一步加深對(duì) Zookeeper 的認(rèn)識(shí),我利用空閑時(shí)間編寫(xiě)了本篇文章對(duì)應(yīng)的 demo –?基于 Zookeeper 的分布式鎖實(shí)現(xiàn)。通過(guò)編寫(xiě)這個(gè)分布式鎖 demo,使我對(duì) Zookeeper 的 watcher 機(jī)制、Zookeeper 的用途等有了更進(jìn)一步的認(rèn)識(shí)。不過(guò)我所編寫(xiě)的分布式鎖還是比較簡(jiǎn)陋的,實(shí)現(xiàn)的也不夠優(yōu)美,僅僅是個(gè)練習(xí),僅供參考使用。好了,題外話就說(shuō)到這里,接下來(lái)我們就來(lái)聊聊基于 Zookeeper 的分布式鎖實(shí)現(xiàn)。
?2. 獨(dú)占鎖和讀寫(xiě)鎖的實(shí)現(xiàn)
在本章,我將分別說(shuō)明獨(dú)占鎖和讀寫(xiě)鎖詳細(xì)的實(shí)現(xiàn)過(guò)程,并配以相應(yīng)的流程圖幫助大家了解實(shí)現(xiàn)的過(guò)程。這里先說(shuō)說(shuō)獨(dú)占鎖的實(shí)現(xiàn)。
?2.1 獨(dú)占鎖的實(shí)現(xiàn)
獨(dú)占鎖又稱排它鎖,從字面意思上很容易理解他們的用途。即如果某個(gè)操作 O1?對(duì)訪問(wèn)資源 R1?的過(guò)程加鎖,在操作 O1結(jié)束對(duì)資源 R1?訪問(wèn)前,其他操作不允許訪問(wèn)資源 R1。以上算是對(duì)獨(dú)占鎖的簡(jiǎn)單定義了,那么這段定義在 Zookeeper 的“類 Unix/Linux 文件系統(tǒng)”的結(jié)構(gòu)中是怎樣實(shí)現(xiàn)的呢?在鎖答案前,我們先看張圖:
圖1 獨(dú)占鎖的 Zookeeper 節(jié)點(diǎn)結(jié)構(gòu)
如上圖,對(duì)于獨(dú)占鎖,我們可以將資源 R1?看做是 lock 節(jié)點(diǎn),操作 O1?訪問(wèn)資源 R1?看做創(chuàng)建 lock 節(jié)點(diǎn),釋放資源 R1看做刪除 lock 節(jié)點(diǎn)。這樣我們就將獨(dú)占鎖的定義對(duì)應(yīng)于具體的 Zookeeper 節(jié)點(diǎn)結(jié)構(gòu),通過(guò)創(chuàng)建 lock 節(jié)點(diǎn)獲取鎖,刪除節(jié)點(diǎn)釋放鎖。詳細(xì)的過(guò)程如下:
上面即獨(dú)占鎖具體的實(shí)現(xiàn)步驟,理解起來(lái)并不復(fù)雜,這里不再贅述。
圖2 獲取獨(dú)占鎖流程圖
?2.2 讀寫(xiě)鎖的實(shí)現(xiàn)
說(shuō)完獨(dú)占鎖的實(shí)現(xiàn),這節(jié)來(lái)說(shuō)說(shuō)讀寫(xiě)鎖的實(shí)現(xiàn)。讀寫(xiě)鎖包含一個(gè)讀鎖和寫(xiě)鎖,操作 O1?對(duì)資源 R1?加讀鎖,且獲得了鎖,其他操作可同時(shí)對(duì)資源 R1?設(shè)置讀鎖,進(jìn)行共享讀操作。如果操作 O1?對(duì)資源 R1?加寫(xiě)鎖,且獲得了鎖,其他操作再對(duì)資源 R1?設(shè)置不同類型的鎖都會(huì)被阻塞。總結(jié)來(lái)說(shuō),讀鎖具有共享性,而寫(xiě)鎖具有排他性。那么在 Zookeeper 中,我們可以用怎樣的節(jié)點(diǎn)結(jié)構(gòu)實(shí)現(xiàn)上面的操作呢?
圖3 讀寫(xiě)鎖的 Zookeeper 節(jié)點(diǎn)結(jié)構(gòu)
在 Zookeeper 中,由于讀寫(xiě)鎖和獨(dú)占鎖的節(jié)點(diǎn)結(jié)構(gòu)不同,讀寫(xiě)鎖的客戶端不用再去競(jìng)爭(zhēng)創(chuàng)建 lock 節(jié)點(diǎn)。所以在一開(kāi)始,所有的客戶端都會(huì)創(chuàng)建自己的鎖節(jié)點(diǎn)。如果不出意外,所有的鎖節(jié)點(diǎn)都能被創(chuàng)建成功,此時(shí)鎖節(jié)點(diǎn)結(jié)構(gòu)如圖3所示。之后,客戶端從 Zookeeper 端獲取 /share_lock 下所有的子節(jié)點(diǎn),并判斷自己能否獲取鎖。如果客戶端創(chuàng)建的是讀鎖節(jié)點(diǎn),獲取鎖的條件(滿足其中一個(gè)即可)如下:
如果客戶端創(chuàng)建的是寫(xiě)鎖節(jié)點(diǎn),由于寫(xiě)鎖具有排他性。所以獲取鎖的條件要簡(jiǎn)單一些,只需確定自己創(chuàng)建的鎖節(jié)點(diǎn)是否排在其他子節(jié)點(diǎn)前面即可。
不同于獨(dú)占鎖,讀寫(xiě)鎖的實(shí)現(xiàn)稍微復(fù)雜一下。讀寫(xiě)鎖有兩種實(shí)現(xiàn)方式,各有異同,接下來(lái)就來(lái)說(shuō)說(shuō)這兩種實(shí)現(xiàn)方式。
?讀寫(xiě)鎖的第一種實(shí)現(xiàn)
第一種實(shí)現(xiàn)是對(duì) /share_lock 節(jié)點(diǎn)設(shè)置 watcher,當(dāng) /share_lock 下的子節(jié)點(diǎn)被刪除時(shí),未獲取鎖的客戶端收到 /share_lock 子節(jié)點(diǎn)變動(dòng)的通知。在收到通知后,客戶端重新判斷自己創(chuàng)建的子節(jié)點(diǎn)是否可以獲取鎖,如果失敗,再次等待通知。詳細(xì)流程如下:
上述步驟對(duì)于的流程圖如下:
圖4 獲取讀寫(xiě)鎖實(shí)現(xiàn)1流程圖
上面獲取讀寫(xiě)鎖流程并不復(fù)雜,但卻存在性能問(wèn)題。以圖3所示鎖節(jié)點(diǎn)結(jié)構(gòu)為例,第一個(gè)鎖節(jié)點(diǎn) host1-W-0000000001 被移除后,Zookeeper 會(huì)將 /share_lock 子節(jié)點(diǎn)變動(dòng)的通知分發(fā)給所有的客戶端。但實(shí)際上,該子節(jié)點(diǎn)變動(dòng)通知除了能影響 host2-R-0000000002 節(jié)點(diǎn)對(duì)應(yīng)的客戶端外,分發(fā)給其他客戶端則是在做無(wú)用功,因?yàn)槠渌蛻舳思词公@取了通知也無(wú)法獲取鎖。所以這里需要做一些優(yōu)化,優(yōu)化措施是讓客戶端只在自己關(guān)心的節(jié)點(diǎn)被刪除時(shí),再去獲取鎖。
?讀寫(xiě)鎖的第二種實(shí)現(xiàn)
在了解讀寫(xiě)鎖第一種實(shí)現(xiàn)的弊端后,我們針對(duì)這一實(shí)現(xiàn)進(jìn)行優(yōu)化。這里客戶端不再對(duì) /share_lock 節(jié)點(diǎn)進(jìn)行監(jiān)視,而只對(duì)自己關(guān)心的節(jié)點(diǎn)進(jìn)行監(jiān)視。還是以圖3的鎖節(jié)點(diǎn)結(jié)構(gòu)進(jìn)行舉例說(shuō)明,host2-R-0000000002 對(duì)應(yīng)的客戶端 C2?只需監(jiān)視 host1-W-0000000001 節(jié)點(diǎn)是否被刪除即可。而 host3-W-0000000003 對(duì)應(yīng)的客戶端 C3?只需監(jiān)視 host2-R-0000000002 節(jié)點(diǎn)是否被刪除即可,只有 host2-R-0000000002 節(jié)點(diǎn)被刪除,客戶端 C3?才能獲取鎖。而 host1-W-0000000001 節(jié)點(diǎn)被刪除時(shí),產(chǎn)生的通知對(duì)于客戶端 C3?來(lái)說(shuō)是無(wú)用的,即使客戶端 C3?響應(yīng)了通知也沒(méi)法獲取鎖。這里總結(jié)一下,不同客戶端關(guān)心的鎖節(jié)點(diǎn)是不同的。如果客戶端創(chuàng)建的是讀鎖節(jié)點(diǎn),那么客戶端只需找出比讀鎖節(jié)點(diǎn)序號(hào)小的最后一個(gè)的寫(xiě)鎖節(jié)點(diǎn),并設(shè)置 watcher 即可。而如果是寫(xiě)鎖節(jié)點(diǎn),則更簡(jiǎn)單,客戶端僅需對(duì)該節(jié)點(diǎn)的上一個(gè)節(jié)點(diǎn)設(shè)置 watcher 即可。詳細(xì)的流程如下:
上述步驟對(duì)于的流程圖如下:
圖5 獲取讀寫(xiě)鎖實(shí)現(xiàn)2流程圖
?3. 寫(xiě)在最后
本文較為詳細(xì)的描述了基于 Zookeeper 分布式鎖的實(shí)現(xiàn)過(guò)程,并根據(jù)上面描述的兩種鎖原理實(shí)現(xiàn)了較為簡(jiǎn)單的分布式鎖 demo,代碼放在了?github?上,需要的朋友自取。因?yàn)檫@只是一個(gè)簡(jiǎn)單的 demo,代碼實(shí)現(xiàn)的并不優(yōu)美,僅供參考。最后,如果你覺(jué)得文章還不錯(cuò)的話,歡迎點(diǎn)贊。如果有不妥的地方,也請(qǐng)?zhí)岢鰜?lái),我會(huì)虛心改之。好了,最后祝大家生活愉快,再見(jiàn)。
?參考
- 《ZooKeeper 分布式過(guò)程協(xié)同技術(shù)詳解》
- 《從Paxos到Zookeeper 分布式一致性原理與實(shí)踐》
- 本文鏈接:?https://www.tianxiaobo.com/2018/01/20/基于-Zookeeper-的分布式鎖實(shí)現(xiàn)/
from:http://www.tianxiaobo.com/2018/01/20/%E5%9F%BA%E4%BA%8E-Zookeeper-%E7%9A%84%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E5%AE%9E%E7%8E%B0/?
總結(jié)
以上是生活随笔為你收集整理的基于 Zookeeper 的分布式锁实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 自己动手实现的 Spring IOC 和
- 下一篇: LinkedHashMap 源码详细分析