最近在學習 Zookeeper,在剛開始接觸 Zookeeper 的時候,徹底不知道 Zookeeper 有什麼用。且不少資料都是將 Zookeeper 描述成一個「類 Unix/Linux 文件系統」的中間件,致使我很難將類 Unix/Linux 文件系統的 Zookeeper 和分佈式應用聯繫在一塊兒。後來在粗讀了《ZooKeeper 分佈式過程協同技術詳解》和《從Paxos到Zookeeper 分佈式一致性原理與實踐》兩本書,並動手寫了一些 CURD demo 後,初步對 Zookeeper 有了必定的瞭解。不過比較膚淺,爲了進一步加深對 Zookeeper 的認識,我利用空閒時間編寫了本篇文章對應的 demo -- 基於 Zookeeper 的分佈式鎖實現。經過編寫這個分佈式鎖 demo,使我對 Zookeeper 的 watcher 機制、Zookeeper 的用途等有了更進一步的認識。不過我所編寫的分佈式鎖仍是比較簡陋的,實現的也不夠優美,僅僅是個練習,僅供參考使用。好了,題外話就說到這裏,接下來咱們就來聊聊基於 Zookeeper 的分佈式鎖實現。git
在本章,我將分別說明獨佔鎖和讀寫鎖詳細的實現過程,並配以相應的流程圖幫助你們瞭解實現的過程。這裏先說說獨佔鎖的實現。github
獨佔鎖又稱排它鎖,從字面意思上很容易理解他們的用途。即若是某個操做 O1 對訪問資源 R1 的過程加鎖,在操做 O1 結束對資源 R1 訪問前,其餘操做不容許訪問資源 R1。以上算是對獨佔鎖的簡單定義了,那麼這段定義在 Zookeeper 的「類 Unix/Linux 文件系統」的結構中是怎樣實現的呢?在鎖答案前,咱們先看張圖:分佈式
圖1 獨佔鎖的 Zookeeper 節點結構性能
如上圖,對於獨佔鎖,咱們能夠將資源 R1 看作是 lock 節點,操做 O1 訪問資源 R1 看作建立 lock 節點,釋放資源 R1 看作刪除 lock 節點。這樣咱們就將獨佔鎖的定義對應於具體的 Zookeeper 節點結構,經過建立 lock 節點獲取鎖,刪除節點釋放鎖。詳細的過程以下:學習
上面即獨佔鎖具體的實現步驟,理解起來並不複雜,這裏再也不贅述。優化
圖2 獲取獨佔鎖流程圖spa
說完獨佔鎖的實現,這節來講說讀寫鎖的實現。讀寫鎖包含一個讀鎖和寫鎖,操做 O1 對資源 R1 加讀鎖,且得到了鎖,其餘操做可同時對資源 R1 設置讀鎖,進行共享讀操做。若是操做 O1 對資源 R1 加寫鎖,且得到了鎖,其餘操做再對資源 R1 設置不一樣類型的鎖都會被阻塞。總結來講,讀鎖具備共享性,而寫鎖具備排他性。那麼在 Zookeeper 中,咱們能夠用怎樣的節點結構實現上面的操做呢?code
圖3 讀寫鎖的 Zookeeper 節點結構中間件
在 Zookeeper 中,因爲讀寫鎖和獨佔鎖的節點結構不一樣,讀寫鎖的客戶端不用再去競爭建立 lock 節點。因此在一開始,全部的客戶端都會建立本身的鎖節點。若是不出意外,全部的鎖節點都能被建立成功,此時鎖節點結構如圖3所示。以後,客戶端從 Zookeeper 端獲取 /share_lock 下全部的子節點,並判斷本身可否獲取鎖。若是客戶端建立的是讀鎖節點,獲取鎖的條件(知足其中一個便可)以下:blog
若是客戶端建立的是寫鎖節點,因爲寫鎖具備排他性。因此獲取鎖的條件要簡單一些,只需肯定本身建立的鎖節點是否排在其餘子節點前面便可。
不一樣於獨佔鎖,讀寫鎖的實現稍微複雜一下。讀寫鎖有兩種實現方式,各有異同,接下來就來講說這兩種實現方式。
第一種實現是對 /share_lock 節點設置 watcher,當 /share_lock 下的子節點被刪除時,未獲取鎖的客戶端收到 /share_lock 子節點變更的通知。在收到通知後,客戶端從新判斷本身建立的子節點是否能夠獲取鎖,若是失敗,再次等待通知。詳細流程以下:
上述步驟對於的流程圖以下:
圖4 獲取讀寫鎖實現1流程圖
上面獲取讀寫鎖流程並不複雜,但卻存在性能問題。以圖3所示鎖節點結構爲例,第一個鎖節點 host1-W-0000000001 被移除後,Zookeeper 會將 /share_lock 子節點變更的通知分發給全部的客戶端。但實際上,該子節點變更通知除了能影響 host2-R-0000000002 節點對應的客戶端外,分發給其餘客戶端則是在作無用功,由於其餘客戶端即便獲取了通知也沒法獲取鎖。因此這裏須要作一些優化,優化措施是讓客戶端只在本身關心的節點被刪除時,再去獲取鎖。
在瞭解讀寫鎖第一種實現的弊端後,咱們針對這一實現進行優化。這裏客戶端再也不對 /share_lock 節點進行監視,而只對本身關心的節點進行監視。仍是以圖3的鎖節點結構進行舉例說明,host2-R-0000000002 對應的客戶端 C2 只需監視 host1-W-0000000001 節點是否被刪除便可。而 host3-W-0000000003 對應的客戶端 C3 只需監視 host2-R-0000000002 節點是否被刪除便可,只有 host2-R-0000000002 節點被刪除,客戶端 C3 才能獲取鎖。而 host1-W-0000000001 節點被刪除時,產生的通知對於客戶端 C3 來講是無用的,即便客戶端 C3 響應了通知也無法獲取鎖。這裏總結一下,不一樣客戶端關心的鎖節點是不一樣的。若是客戶端建立的是讀鎖節點,那麼客戶端只需找出比讀鎖節點序號小的最後一個的寫鎖節點,並設置 watcher 便可。而若是是寫鎖節點,則更簡單,客戶端僅需對該節點的上一個節點設置 watcher 便可。詳細的流程以下:
上述步驟對於的流程圖以下:
圖5 獲取讀寫鎖實現2流程圖
本文較爲詳細的描述了基於 Zookeeper 分佈式鎖的實現過程,並根據上面描述的兩種鎖原理實現了較爲簡單的分佈式鎖 demo,代碼放在了 github 上,須要的朋友自取。由於這只是一個簡單的 demo,代碼實現的並不優美,僅供參考。最後,若是你以爲文章還不錯的話,歡迎點贊。若是有不妥的地方,也請提出來,我會虛心改之。好了,最後祝你們生活愉快,再見。
本文在知識共享許可協議 4.0 下發布,轉載請註明出處
做者:coolblog
爲了得到更好的分類閱讀體驗,
請移步至本人的我的博客: http://www.coolblog.xyz
本做品採用知識共享署名-非商業性使用-禁止演繹 4.0 國際許可協議進行許可。