ZooKeeper典型應用場景
ZooKeeper是一個高可用的分散式資料管理與系統協調框架。基於對Paxos演算法的實現,使該框架保證了分散式環境中資料的強一致性,也正是基於這樣的特性,使得ZooKeeper解決很多分散式問題。網上對ZK的應用場景也有不少介紹,本文將結合作者身邊的專案例子,系統地對ZK的應用場景進行一個分門歸類的介紹。
值得注意的是,ZK並非天生就是為這些應用場景設計的,都是後來眾多開發者根據其框架的特性,利用其提供的一系列API介面(或者稱為原語集),摸索出來的典型使用方法。因此,也非常歡迎讀者分享你在ZK使用上的奇技淫巧。
資料釋出與訂閱(配置中心) |
釋出與訂閱模型,即所謂的配置中心,顧名思義就是釋出者將資料釋出到 |
注意:在上面提到的應用場景中,有個預設前提是:資料量很小,但是資料更新可能會比較快的場景。 |
負載均衡 |
這裡說的負載均衡是指軟負載均衡。在分散式環境中,為了保證高可用性,通常同一個應用或同一個服務的提供方都會部署多份,達到對等服務。而消費者就須要在這些對等的伺服器中選擇一個來執行相關的業務邏輯,其中比較典型的是訊息中介軟體中的生產者,消費者負載均衡。 |
訊息中介軟體中釋出者和訂閱者的負載均衡,linkedin開源的KafkaMQ和阿里開源的metaq都是通過zookeeper來做到生產者、消費者的負載均衡。這裡以metaq為例如講下:生產者負載均衡:metaq傳送訊息的時候,生產者在傳送訊息的時候必須選擇一臺broker上的一個分割槽來發送訊息,因此metaq在執行過程中,會把所有broker和對應的分割槽資訊全部註冊到ZK指定節點上,預設的策略是一個依次輪詢的過程,生產者在通過ZK獲取分割槽列表之後,會按照brokerId和partition的順序排列組織成一個有序的分割槽列表,傳送的時候按照從頭到尾迴圈往復的方式選擇一個分割槽來發送訊息。 消費負載均衡: 在消費過程中,一個消費者會消費一個或多個分割槽中的訊息,但是一個分割槽只會由一個消費者來消費。MetaQ的消費策略是:
在某個消費者故障或者重啟等情況下,其他消費者會感知到這一變化(通過 zookeeper watch消費者列表),然後重新進行負載均衡,保證所有的分割槽都有消費者進行消費。 |
命名服務(Naming Service) |
命名服務也是分散式系統中比較常見的一類場景。在分散式系統中,通過使用命名服務,客戶端應用能夠根據指定名字來獲取資源或服務的地址,提供者等資訊。被命名的實體通常可以是叢集中的機器,提供的服務地址,遠端物件等等——這些我們都可以統稱他們為名字(Name)。其中較為常見的就是一些分散式服務框架中的服務地址列表。通過呼叫ZK提供的建立節點的 API,能夠很容易建立一個全域性唯一的path,這個path就可以作為一個名稱。 |
阿里巴巴集團開源的分散式服務框架Dubbo中使用ZooKeeper來作為其命名服務,維護全域性的服務地址列表,點選這裡檢視Dubbo開源專案。在Dubbo實現中: 服務提供者在啟動的時候,向ZK上的指定節點/dubbo/${serviceName}/providers目錄下寫入自己的URL地址,這個操作就完成了服務的釋出。 服務消費者啟動的時候,訂閱/dubbo/${serviceName}/providers目錄下的提供者URL地址, 並向/dubbo/${serviceName} /consumers目錄下寫入自己的URL地址。 注意,所有向ZK上註冊的地址都是臨時節點,這樣就能夠保證服務提供者和消費者能夠自動感應資源的變化。 另外,Dubbo還有針對服務粒度的監控,方法是訂閱/dubbo/${serviceName}目錄下所有提供者和消費者的資訊。 |
分散式通知/協調 |
ZooKeeper中特有watcher註冊與非同步通知機制,能夠很好的實現分散式環境下不同系統之間的通知與協調,實現對資料變更的實時處理。使用方法通常是不同系統都對ZK上同一個znode進行註冊,監聽znode的變化(包括znode本身內容及子節點的),其中一個系統update了znode,那麼另一個系統能夠收到通知,並作出相應處理 |
總之,使用zookeeper來進行分散式通知和協調能夠大大降低系統之間的耦合 |
叢集管理與Master選舉 |
利用ZooKeeper有兩個特性,就可以實時另一種叢集機器存活性監控系統:
例如,監控系統在 /clusterServers 節點上註冊一個Watcher,以後每動態加機器,那麼就往 /clusterServers 下建立一個 EPHEMERAL型別的節點:/clusterServers/{hostname}. 這樣,監控系統就能夠實時知道機器的增減情況,至於後續處理就是監控系統的業務了。
在分散式環境中,相同的業務應用分佈在不同的機器上,有些業務邏輯(例如一些耗時的計算,網路I/O處理),往往只需要讓整個叢集中的某一臺機器進行執行,其餘機器可以共享這個結果,這樣可以大大減少重複勞動,提高效能,於是這個master選舉便是這種場景下的碰到的主要問題。 利用ZooKeeper的強一致性,能夠保證在分散式高併發情況下節點建立的全域性唯一性,即:同時有多個客戶端請求建立 /currentMaster 節點,最終一定只有一個客戶端請求能夠建立成功。利用這個特性,就能很輕易的在分散式環境中進行叢集選取了。 另外,這種場景演化一下,就是動態Master選舉。這就要用到?EPHEMERAL_SEQUENTIAL型別節點的特性了。 上文中提到,所有客戶端建立請求,最終只有一個能夠建立成功。在這裡稍微變化下,就是允許所有請求都能夠建立成功,但是得有個建立順序,於是所有的請求最終在ZK上建立結果的一種可能情況是這樣: /currentMaster/{sessionId}-1 ,?/currentMaster/{sessionId}-2 ,?/currentMaster/{sessionId}-3 ….. 每次選取序列號最小的那個機器作為Master,如果這個機器掛了,由於他建立的節點會馬上小時,那麼之後最小的那個機器就是Master了。 |
|
分散式鎖 |
分散式鎖,這個主要得益於ZooKeeper為我們保證了資料的強一致性。鎖服務可以分為兩類,一個是保持獨佔,另一個是控制時序。
|
分散式佇列 |
佇列方面,簡單地講有兩種,一種是常規的先進先出佇列,另一種是要等到佇列成員聚齊之後的才統一按序執行。對於第一種先進先出佇列,和分散式鎖服務中的控制時序場景基本原理一致,這裡不再贅述。 第二種佇列其實是在FIFO佇列的基礎上作了一個增強。通常可以在 /queue 這個znode下預先建立一個/queue/num 節點,並且賦值為n(或者直接給/queue賦值n),表示佇列大小,之後每次有佇列成員加入後,就判斷下是否已經到達佇列大小,決定是否可以開始執行了。這種用法的典型場景是,分散式環境中,一個大任務Task A,需要在很多子任務完成(或條件就緒)情況下才能進行。這個時候,凡是其中一個子任務完成(就緒),那麼就去 /taskList 下建立自己的臨時時序節點(CreateMode.EPHEMERAL_SEQUENTIAL),當 /taskList 發現自己下面的子節點滿足指定個數,就可以進行下一步按序進行處理了。 |