開源組件系列(8):分佈式結構化存儲(Zookeeper)

目錄

(一)分佈式協調服務的意義

(二)Zookeeper數據模型

(三)Zookeeper基本架構

(四)Zookeeper應用案例:Leader選舉


(一)分佈式協調服務的意義

 

分佈式協調服務在分佈式應用中是不可缺少的,通過引入類似於文件系統的層級命名空間,並在此基礎上提供一套簡單易懂的規範語言,能夠幫助用戶輕易的實現諸如Leader選舉、分佈式鎖及分佈式隊列等功能。Zookeeper已經廣泛的應用在開源系統中,包括HDFS、Yarn、HBase等組件中。接下來通過Leader選舉和負載均衡爲例,說明分佈式協調服務存在的意義和基本職責。

 

Leader選舉:

在分佈式系統中,常見的一種軟件設計架構爲Master/Slave,其中Master負責集羣管理,Slave負責執行具體的任務(存儲、處理數據等)。這種架構存在一種明顯的缺陷:Master是單點,如果出現問題,將影響整個軟件架構的使用。爲了避免Master出現故障,常見的優化方案是引入多個Master,例如雙Master模式:Active和Standby,如下圖所示:

 

引入雙Master需要解決兩個問題:一個是如何選舉出一個Master作爲Active Master;另一個是如果Active Master出現了問題,如何將Standby Master安全切換爲Active Master。爲了避免單獨開發分佈式系統造成功能的冗餘,因此引入Zookeeper提供基本可靠的協調服務勢在必行,提供諸如選舉和服務狀態獲取等基本功能。

 

負載均衡:

在Kakfa等分佈式消息隊列中,生產者將數據寫入分佈式隊列,消費者從分佈式消息隊列中讀取數據進行處理,因此就產生了兩個基本問題:第一,由於消息隊列是分佈式的,節點的健康狀態是動態變化的,因此生產者和消費者如何獲得最新的消息隊列位置?第二,消息隊列提供了一組可存儲數據的節點,如何讓生產者將數據均衡地寫入消息隊列的各個節點?如下圖所示:

爲了解決以上兩個問題,如果引入一個可靠的分佈式協調服務,它需要具備簡單的元信息存儲和動態獲取服務狀態等功能。因此引入Zookeeper,可以有效的將服務協調的職責從分佈式系統中獨立出來,減少系統的耦合性。

 

(二)Zookeeper數據模型

 

考慮到分佈式協調服務內部實現的複雜性,Zookeeper將盡可能簡單的數據模型和API暴露給用戶,以屏蔽協調服務本身的複雜性。Zookeeper提供了類似於文件系統的層級命名空間,而所有分佈式協調功能均可以藉助作用在該命名空間上的原語來實現。相關模型如下:

 

1.層級命名空間

下圖給出了一個典型的Zookeeper層級命名空間,整個命名的方式類似於文件系統,以多叉樹形式組織在一起。其中,每個節點被稱爲“znode”,主要包括以下幾種屬性:

(1)data:每個znode擁有一個數據域,記錄了用戶的數據,類型爲字節數組,通過多副本的方式保證數據的存儲可靠性;

(2)type:znode類型,具體分爲持久化節點、臨時節點、順序增量三種類型;

(3)version:znode中的數據版本號,每次數據更新版本號將+1;

(4)children:znode可以包含子節點;

(5)ACL:znode訪問控制列表,用戶可以單獨設置每個znode的可訪問用戶。

 

2.Watcher

Watcher是Zookeeper提供的發佈/訂閱機制,用戶可以在某個znode上註冊watcher以監聽它的變化,一旦對應的znode被刪除或者更新,Zookeeper將以事件的形式將變化內容發送給監聽者。需要注意的是,Watcher一旦觸發後便會被刪除,除非該用戶再次註冊該Watcher。

 

3.Session

Session是Zookeeper中的一個重要概念,它是客戶端與Zookeeper服務端之間通信通道,同一個Session中的消息是有序的。Session具有容錯性:如果客戶端連接的Zookeeper服務器宕機,客戶端會自動連接到其他活着的服務器上。

 

(三)Zookeeper基本架構

 

Zookeeper服務通常由奇數個Zookeeper實例構成,其中一個實例爲Leader角色,其他爲Follower角色,它們同時維護了層級目錄結構的一個副本,並通過ZAB協議維持副本之間的一致性。Zookeeper將所有數據保存在內存中,因而具有高吞吐率和低延遲的有點,如下圖所示:

Zookeeper讀寫數據的路徑如下:

讀路徑:任意一個Zookeeper實例均可以爲客戶端提供讀服務,實例數目越多,讀吞吐率越高;

寫路徑:任意一個Zookeeper實例均可以接受客戶端的寫請求,但需要進一步轉發給Leader協調完成分佈式寫。Zookeeper採用了簡化版的Paxos協議,也稱之爲ZAB協議,只要多數Zookeeper實例寫成功,就認爲本次寫是成功的。如果一個集羣存在2N+1個Zookeeper實例,只要其中的N+1個實例寫成功,則本次寫操作便成功了。從容錯性角度看,集羣最大容忍失敗實例數目爲N。

值得注意的是,Zookeeper實例數目越多,寫延遲越高。當Leader出現故障時,Zookeeper會通過ZAB協議發起新一輪的Leader投票選舉,保證集羣中始終有一個可用的Leader。

Zookeeper中多個實例的內存數據並不是強一致的,ZAB協議只能保證同一時刻內至少多數節點中的數據是強一致的,爲了讓客戶端讀到最新的數據,需要給對應的Zookeeper實例發送通過指令,強制與Leader同步數據。

在Zookeeper集羣中,隨着Zookeeper實例數目的增多,讀吞吐率升高,但寫延遲也隨之增加。爲了解決集羣擴展性導致的寫性能下降問題,Zookeeper引入了第三個角色:Observer,Observer並不參與投票過程,其功能與Follower類似,可以接入正常的Zookeeper集羣,接受並處理客戶端讀請求,或者將寫請求進一步轉發給Leader處理。由於Observer自身能夠保存已存數據提供讀服務,因此可以通過增加Observer實例數來提高系統的讀吞吐率。

 

(四)Zookeeper應用案例:Leader選舉

 

基於Zookeeper實現Leader選舉的基本思想是,讓各個參與競選的實例同時在Zookeeper上創建指定的znode,誰創建成功則誰競選成功,並將自己的信息(host、ip、port等)寫入該znode數據域,之後其他競選者向該znode註冊watcher,便於當前Leader出現故障時,第一時間再次參與競選,如下圖所示:

基於Zookeeper的Leader選舉流程如下:

(1)各實例啓動成功後,嘗試在Zookeeper上創建ephemeral類型的znode節點/current/leader,假設實例B創建成功,則將自己的信息寫入該znode,並將自己的角色標準爲Leader,開始執行Leader相關的初始化工作;

(2)除B之外的實例得知創建znode失敗,則向/current/leader註冊watcher,並將自己角色標註爲Follower,開始執行Follower相關的初始化工作;

(3)系統正常運行,直到實例B因故障推出,此時znode節點/current/leader被Zookeeper刪除,其他Follower收到節點被刪除的事情,重新轉入步驟1,開始新一輪的Leader選舉。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章