前面說了ZooKeeper一些基礎性的東西,包括用戶端程式設計架構。這裡我們來探索如何更好的運用ZooKeeper。
開始之前,我想先借用Linus Torvalds(Linux創始人)的一句話。
Bad programmers worry about the code. Good programmers worry about data structures and their relationships
差的程式員關注代碼,好的程式員關注資料結構和資料之間的關系。

與文無關
不管如何,我們能感受到資料結構的重要性,而平時我們開發所涉及到的各種東西,本質還是:
程式=算法+資料結構。資料結構也是程式設計的基礎,我們這裡說如何通過ZooKeeper建構分布式系統,和它涉及的資料結構。
涉及到的資料結構:
- Barrir (障礙)
- Quene (隊列)
- Lock (鎖)
- Leader Selection
- Group MemberShip
- Service Discovery
這是一篇介紹技術的文章,但是裡面一張圖也沒有,捂臉...
Barrier
Barrier中文譯為栅欄,障礙。意思是暫時阻塞部分東西,直到某個條件滿足。比如:“百米賽跑的起跑線,把起跑線當做障礙,大家還不能跑,隻有人到齊了才能跑”。意思就是滿足某個條件之後才能繼續接下來的操作。
可以參考多線程的:CountDownLatch和CyclicBarrir。
使用ZooKeeper算法實作:(使用僞代碼)
- 首先,建立一個障礙節點 /zk_barrier
- 如果障礙節點存在的話,也就是系統中是存在某個障礙,需要滿足某個條件的。
- 用戶端在/zk_barrier節點調用exists()方法,注冊watch事件。
- 如果exists()方法傳回為false,也就是條件滿足,用戶端可以進行接下來的操作了。
- 如果exists()方法傳回為true,用戶端就等待watch的事件。
- 當障礙節點需要的條件滿足的時候,負責/zk_barrier節點的用戶端會删除這個節點。
- 删除節點觸發watch事件,用戶端再次調用exists()方法再障礙節點上。
- 如果exists()傳回為true的話,用戶端繼續原本的操作,等待條件滿足。
上面的這個結構相對比較簡單,還有種結構用來 鎖住分布式計算的開始和結束,也叫做Double Barrier. 雙重障礙的實作,當節點的數量滿足條件的時候,開始任務,當節點的數量為0的時候,結束任務。
算法實作:
第一部分:
- 首先建立一個/barrier節點,其餘用戶端在障礙節點上注冊事件,并且在/barrier節點下面建立臨時節點,也就是以/barrier節點為父節點。 新建立的節點一般是用戶端自己的主機名。
- 用戶端同時注冊一個事件,檢查/barrier/ready節點是否存在。
- 系統中已經預定義了 N 數字,這個數目為任務開始的最小滿足節點數。節點的數目必須要等于或多與N的時候,才開始任務。
-
當有新的節點加入的時候,新的節點都擷取一下目前/barrier節點下子節點的數目。注意getChildren沒有設定監聽事件。
M = getChildren(/barrier, watch=false)
- 如果M小于N,繼續等待ready節點的出現。
- 如果M等于N,然後這個用戶端在/barrier節點下建立/barrier/ready節點。
-
由于之前有節點注冊了監聽事件,每個用戶端都會開始任務。
第二部分:
- 用戶端完成它需要完成的任務之後,删除它在/barrier節點下建立的子節點。
-
删除完之後,用戶端再調用
M = getChildren(/barrier, watch=true)。
如果M大于0,用戶端仍然等待通知。
如果M等于0,用戶端退出。
上面可能有個風險是網絡流量過于龐大,一旦ready節點注冊,它像所有的節點發送通知。有個辦法是每個用戶端注冊ready的下一個最小的序列臨時節點。這樣每次隻有一個節點收到通知。不會一下子所有的節點都被喚醒。
Queue
分布式隊列也是分布式系統中一種非常常見的資料結構,一種比較特殊的隊列叫做。生産者-消費者 隊列。有一個集合,生産者生産的東西存放在這個集合裡面,消費者從集合中拿出東西來消費。它遵守FIFO(先進先出原則)。
直接看僞代碼:
- 建立一個/QUEUE節點,用作實作隊列的集合。
- 作為生産者的用戶端,調用create()方法建立名為"queue-"的節點,這個節點是臨時節點,同時是序列節點,并且是/QUEUE的子節點。一般節點名稱像
。queue-N
- 作為消費者的用戶端,在/QUEUE節點上調用getChildren()方法,同時設定監聽事件。
M = getChildren(/_QUEUE_, true)
對子節點排序,拿出數字最小的節點進行消費。然後删除最小的那個節點。
有可能在删除節點的時候,因為有其它的節點在對這個節點進行通路,導緻删除失敗,這時候我恩需要再次重試删除
- 消費者的用戶端不斷的從子節點清單中取出節點進行消費,一旦清單中的節點都被消費了。消費者重新調用getChildren方法。
- 直到getChildren()傳回為空的清單的時候,這意味着/QUEUE節點下沒有更多的節點了。
有更多精力的話可以實作優先級隊列。
注意:建議看一下ZooKeeper官方對這兩種資料結構的實作位址為
http://zookeeper.apache.org/doc/r3.4.6/zookeeperTutorial.html分布式鎖
分布式鎖也就是意味着在任何時間,系統中最多隻能有一個用戶端持有這把鎖。一般在下面場景的時候有用:
- 寫入共享資料庫或檔案
- 處理I/O請求
簡單描述一下共享鎖:假設在lock-node下同時建立了三個子節點,l1,l2,l3。那麼建立l1的子節點擁有鎖。當用戶端想釋放鎖的時候,隻需要删除l1節點,然後建立l2的用戶端就擁有了鎖,依次類推。
僞代碼實作:
第一部分:擷取鎖
- 在/locknode下建立子節點,這個節點是 臨時有序的,
create("/_locknode_/lock-",CreateMode=EPHEMERAL_SEQUENTIAL)
- 調用getChilren(/locknode/lock-,false)方法,不設定監聽器。
- 檢查擷取到的節點清單,如果自己建立的節點擁有最小的序号,那麼目前用戶端擁有鎖。退出算法。
- 調用
方法exists("/_locknode_/<最小的節點>, True)
- 如果exists傳回為false,那麼重新進行第2步。
- 如果exists為true,那麼等待存在的這個節點的 watch事件。然後進行第4步。
第二部分:釋放鎖
- 持有鎖的用戶端删除節點,觸發下一個節點擷取鎖。
- 比目前節點序号大的程度最小節點将會擷取到鎖。
Leader選取
Leader選取算法有兩個要求:
- 大多數時間内,隻有一個Leader
- 在任何時間内,要麼沒有Leader要麼隻有一個Leader
暫時我們先把Leader選取算法了解為鎖算法,誰擁有了鎖,誰就是Leader。關于Leader選取算法還需要深入分析。
Group Membership 成員關系
組員關系也是分布式系統的核心元件,目的是為了維護服務的可靠性和一緻性。它可以讓組内的任何一個實體知道目前組的狀況,誰加入了進來,誰離開了。
實作起來較為簡單,僞代碼如下:
- 建立/Membership持久節點,代表成員關系樹
- 加入組的節點在/Membership節點下建立臨時節點。
- 所有的組内成員都有注冊/Membership的監聽事件,是以可以知悉/Membership節點下的變化
- 當有新的節點加入進來,其它的節點會收到通知。當有節點離開,或故障了,ZooKeeper也會自動的删除這個節點,所有的組員也會知道。
- 當有節點想知道其它節點的狀态。使用getChildren()擷取/Membership節點下的子節點即可。
服務發現
如果有認真看上面的幾種實作,我想大部分人都該了解ZooKeeper的套路了,服務發現,也就是我們要知道提供某個服務的伺服器有哪些。我們去找誰來提供服務。
可以了解為“物以類聚,人以群分”,這樣一群一群的,就變成了上面的成員關系管理。資料庫服務的一組,web服務的一組。檔案伺服器的一組,想要知道這個組内的狀态,擷取這個組的節點就好了。比如web伺服器,可以建立/web_service節點,然後web服務都去這個節點找。
其餘的就不多說了。和成員關系基本一樣
實作
光有理論不去實作怎麼行呢,代碼貼出來一方面不太好講解,程式設計靠的是思想,你把東西想明白了,開發起來才容易,自己都想不明白的事情,又如何指望計算機來幫你處理呢。
關于本次所有涉及的理論部分,實作的地方有兩處:
- ZooKeeper的github項目實作 https://github.com/apache/zookeeper/tree/master/src/recipes ,它實作了Quene,Lock,Leader Selection
- Apache Curaror直接給我們封裝好了常用的分布式資料結構,追求快速的話,可以直接使用Apache Curator. maven包為org.apache.curator.curator-recipes,貌似curator也實作了服務發現。感興趣的都可以自己看。
最後
這次主要還是理論偏多,但是大型項目所依賴的也不過是這些基礎的部分。關于ZooKeeper的應用場景,如何使用到這些資料結構的,下面我們再談。
參考
- 《Apache ZooKeeper Essential》
- ZooKeeper官方文檔