ZooKeeper學習筆記及應用場景梳理

官網文檔地址:https://zookeeper.apache.org/doc/r3.5.4-beta/zookeeperOver.html

概述

Zookeeper從設計模式角度來理解:是一個基於觀察者模式設計的分佈式服務管理框架, 它負責存儲和管理大家都關心的數據, 然後接受觀察者的註冊, 一旦這些數據的狀態發生變化, Zookeeper就將負責通知已經在Zookeeper上註冊的那些觀察者做出相應的反應 , 從而實現集羣中類似Master/Slave管理模式。

Zookeeper 是一個分佈式的服務框架,主要用來解決分佈式集羣中應用系統的協調和一致性問題,它能提供基於類似於文件系統的目錄節點樹方式的數據存儲,但是 Zookeeper 並不是用來專門存儲數據的,它的作用主要是用來維護和監控你存儲的數據的狀態變化。如:統一命名服務、狀態同步服務、集羣管理、分佈式應用配置管理等。

它能夠爲分佈式應用提供高性能和可靠地協調服務,使用ZooKeeper可以大大簡化分佈式協調服務的實現,爲開發分佈式應用極大地降低了成本。協同服務很難正確運行,經常出現競爭危害和死鎖。ZooKeeper 的目的就是降低協同服務實現與維護的成本。

架構及原理

集羣架構

在這裏插入圖片描述
Zookeeper集羣是由一組Server節點組成,這一組Server節點中存在一個角色爲Leader的節點,其他節點都爲Follower。客戶端可以和集羣中的任一Server建立連接,當讀請求時,所有Server都可以直接返回結果;當請求爲數據變更請求時,Follower會將請求轉發給Leader節點,Leader節點接收到數據變更請求後,首先會將變更寫入本地磁盤,當持久化完畢後纔會將變更寫入內存,並將變更後的數據同步到各個Follower。

Zookeeper集羣中一共有以下4種角色:
(1) Leader,負責進行投票的發起和決議,更新狀態和數據
(2) Follower,用於接收客戶端請求並向客戶端返回結果,在選擇Leader時會進行投票
(3) Observer,一種功能和Follower相同,但是它不參與投票過程。它主要是爲了擴展系統,提高讀取速度
(4) Client,客戶端用來發起請求,嚴格地說不屬於Zookeeper集羣

ZooKeeper設計目標

易用性,ZooKeeper允許分佈式的進程彼此通過一個共享的結構化的命名空間作協調,這個命名空間組織結構和標準的文件系統相似。命名空間由數據節點(znodes)組成,類似於文件與目錄,與專爲存儲設計的典型文件系統不同,ZooKeeper的數據都保存在內存中,以獲得高吞吐和低時延的特性。

ZooKeeper實現了高性能、高可用和嚴格有序。高性能意味着可以被應用在大型分佈式系統,高可用則避免了單點故障的風險,嚴格有序保證了複雜的同步原語可以在客戶端實現。

可複製,就像它所協同的分佈式進程一樣,ZooKeeper 的組件也是可複製的。構成 ZooKeeper 服務的服務器必須互相感知,它們各自維護了一張相同的內存狀態鏡像,以及持久存儲的事務日誌與快照,只要大部分的服務器可用,ZooKeeper 的服務就可以正常運行。客戶端會維護與某一臺 ZooKeeper 服務器的 TCP 長連接,並通過該連接進行請求發送,響應接收,監聽事件獲取與心跳檢測。如果當前 TCP 長連接斷開,客戶端會向另一臺服務器發起連接請求。

有序性,ZooKeeper 爲每一次更新標記了一個序號,以反映所有事務的順序。後續操作可以使用該序號實現更高級的抽象,例如同步原語。

速度快,ZooKeeper 在“讀頻繁”工作場景中性能優異,在數千臺集羣中運行,讀寫比爲 10:1 時,性能最好。

核心數據結構

Zookeeper的核心數據結構是如下圖的樹形結構:
在這裏插入圖片描述
ZooKeeper提供的命名空間非常像一個標準的文件系統。一個名字是一系列的被/分開的路徑元素,在ZooKeeper中每一個節點被標示爲一個路徑。

但是和標準的文件系統不同,每一個ZooKeeper中的節點可以有數據和它關聯,子節點也一樣。就像一個文件系統允許一個文件是一個目錄。(ZooKeeper被設計用來存儲協調數據:狀態信息,配置,地址信息,等等。所以存儲在每一個節點的數據通常很小,在字節和千字節的範圍。)我們使用znode來使我們清楚我們正在討論ZooKeeper的數據節點。

  • 節點ZNode存儲同步、協調相關的數據,數據量比較小,比如狀態信息、配置內容、位置信息等。
  • ZNode中存有狀態信息,包括版本號、ACL變更、時間戳等, 每次變更版本號都會遞增。這樣一方面可以基於版本號檢索狀態;另一方面可以實現分佈式的樂觀鎖。
  • ZNode都有ACL,可以限制ZNode的訪問權限
  • ZNode上數據的讀寫都是原子的
  • 客戶端可以在ZNode上設置Watcher監聽,一但該ZNode有數據變更,就會通知客戶端,觸發回調方法【這個地方需要注意,Watcher都是一次性,觸發一次後就失效,持續監聽需要重新註冊】
  • 客戶端和Zookeeper連接建立後就是一次session,Zookeeper支持臨時節點,它和一次session關聯,一但session關閉,節點就被刪除。【可以用臨時節點來實現連通性的檢測】

ZNode可以分爲持久節點和臨時節點兩類。持久節點是指一旦該ZNode被創建了,除非主動進行刪除操作,這個節點就會一直存在;而臨時節點的生命週期會和客戶端會話綁定在一起,一旦客戶端會話失效其所創建的所有臨時節點都會被刪除

ZK還支持客戶端創建節點時指定一個特殊的SEQUENTIAL屬性,這個節點被創建的時候ZK會自動在其節點名後面追加上一個整形數字,該數字是一個由服務端維護的自增數字,以此實現創建名稱自增的順序節點。

擔保

ZooKeeper是非常快和簡單的。儘管它的目標是構建例如同步這樣更復雜服務的基礎,它提供了一組保證它們是:

  • 順序的一致性:來自客戶端的更新將會按照它們發送的順序應用;
  • 原子性:更新要麼成功要麼失敗,沒有部分結果;
  • 單一系統映象:客戶端將會看到服務端相同的視圖,不管它連的是那一個服務端;
  • 可靠性:一旦更新成功,它將一直持續到被下一個客戶端更新覆蓋;
  • 時效性:系統的客戶端視圖保證是最新的在一定的時間內;

簡單API

ZooKeeper 的設計目標之一是提供非常簡單的可編程接口,因此,它只提供如下操作:

  • 創建(creat),在目錄樹中某個位置創建一個節點;
  • 刪除(delete),刪除一個節點;
  • 存在性檢查(exists),測試節點是否存在於某個位置;
  • 獲取數據(get data),從節點中讀取數據;
  • 設置數據(set data),向節點中寫入數據;
  • 獲取子節點(get children),檢索節點的子節點列表;
  • 同步(syc),等待數據被傳播;

工作機制

Zookeeper的核心是Zab(Zookeeper Atomic Broadcast)協議。Zab協議有兩種模式,它們分別是恢復模式和廣播模式。

恢復模式

當Zookeeper集羣啓動或Leader崩潰時,就進入到該模式。該模式需要選舉出新的Leader,選舉算法基於paxos或fastpaxos。

  1. 每個Server啓動以後都會詢問其它的Server投票給誰;
  2. 對於其他Server的詢問,Server每次根據自己的狀態回覆自己推薦,Leader的id和該Server最後處理事務的zxid(zookeeper中的每次變更事務都會被賦予一個順序遞增的zxid,zxid越大說明變更越近),Server剛啓動時都會選擇自己;
  3. 收到Server的回覆後,就計算出zxid最大的那個Server,將該Server的信息設置成下次要投票的Server(如果zxid同樣大,就選擇Server id大的那個);
  4. 計算獲得票數最多的Server,如果該Server的得票數超過半數,則該Server當選Leader,否則繼續投票直到Leader選舉出來。

假設一個Zookeeper集羣有5臺機器,ServerId分別爲1、2、3、4、5,啓動順序按照1、2、3、4、5依次啓動:

  1. Server 1啓動,此時它選擇自己爲leader,同時向外發出投票報文,但收不到任何回覆,選票不過半,啓動機器數不超過集羣的一半(不能正常工作);
  2. Server2啓動,此時由於沒有歷史數據,Server1和Server2會選擇ServerId較大的2位leader,但選票不超過一半,啓動的機器數不超過集羣的一半(不能正常工作);
  3. Server3啓動,此時情況與2類似,Server3會被選爲Leader,但不同的是此時得票過半,並且啓動的機器數超過集羣的一半,所以集羣可以正常工作,Server3被選爲Leader;
  4. Server4啓動,由於此時Server3已經被選爲Leader,所以Server4只能作爲Follower;
  5. Server5啓動,與d)同理,Server5也只能作爲Follower

廣播模式

Leader選舉完畢後,Leader需要與Follower進行數據同步:

  1. leader會開始等待server連接;
  2. follower連接leader,將最大的zxid發送給leader;
  3. leader根據follower的zxid確定同步點;
  4. 完成同步後通知follower 已經成爲uptodate狀態;
  5. follower收到uptodate消息後,就可以重新接受client的請求進行服務了。

問題:對於某個更新請求,Leader通過ZAB協議向3個Follower(a, b, c)發出更新請求, 如果Follower a與Follower b都正常返回,而Follower c 宕機了, 這個更新請求對client來說依然是更新成功的。後來Follower c 機器恢復正常了,可是Follower c 機器上的數據已經是過期了,那Follower c 是如何讓自己機器的數據更新到最新的數據呢?

宕機的Follower c在恢復時會有一個故障恢復階段(即上面提到的恢復模式),在這個階段會主動同步Leader數據,達到一致後纔會重新加入到ZK集羣並參與服務。

ZooKeeper典型應用場景

數據發佈/訂閱(配置中心)

以Dubbo註冊中心爲例,Dubbo是阿里巴巴開源的分佈式服務框架,致力於提供高性能和透明化的遠程服務調用解決方案和基於服務框架展開的完整SOA服務治理方案。
在這裏插入圖片描述
其中服務自動發現是最核心的模塊之一,該模塊提供基於註冊中心的目錄服務,使服務消費方能夠動態的查找服務提供方,讓服務地址透明化,同時服務提供方可以平滑的對機器進行擴容和縮容,註冊中心可以基於其提供的外部接口來實現各種不同類型的註冊中心,例如數據庫、ZooKeeper和Redis等。接下來看一下基於ZooKeeper實現的Dubbo註冊中心。
在這裏插入圖片描述

  • /dubbo: 這是Dubbo在ZK上創建的根節點
  • /dubbo/com.test.testService: 這是服務節點,代表了Dubbo的一個服務;
  • /dubbo/com.test.testService/Providers: 這是服務提供者的根節點,其子節點代表了每個服務的真正提供者;
  • /dubbo/com.test.testService/Consumers: 這是服務消費者的根節點,其子節點代表了每一個服務的真正消費者

Dubbo基於ZK實現註冊中心的工作流程:

  • 服務提供者:在初始化啓動的時候首先在/dubbo/com.test.testService/Providers節點下創建一個子節點,同時寫入自己的url地址,代表這個服務的一個提供者;
  • 服務消費者:在啓動的時候讀取並訂閱ZooKeeper上/dubbo/com.test.testService/Providers節點下的所有子節點,並解析所有提供者的url地址類作爲該服務的地址列表,開始發起正常調用。同時在Consumers節點下創建一個臨時節點,寫入自己的url地址,代表自己是BarService的一個消費者;
  • 監控中心:監控中心是Dubbo服務治理體系的重要一部分,它需要知道一個服務的所有提供者和訂閱者及變化情況。監控中心在啓動的時候會通過ZK的/dubbo/com.test.testService節點來獲取所有提供者和消費者的url地址,並註冊Watcher來監聽其子節點變化情況。

所有服務提供者在ZK上創建的節點都是臨時節點,利用的是臨時節點的生命週期和客戶端會話綁定的特性,一旦提供者機器掛掉無法對外提供服務時該臨時節點就會從ZK上摘除,這樣服務消費者和監控中心都能感知到服務提供者的變化

發佈與訂閱模型,即所謂的配置中心,顧名思義就是發佈者將數據發佈到ZooKeeper節點上,供訂閱者動態獲取數據,實現配置信息的集中式管理和動態更新。例如全局的配置信息,服務式服務框架的服務地址列表等就非常適合使用:

  1. 應用中用到的一些配置信息放到ZooKeeper上進行集中管理。這類場景通常是這樣:應用在啓動的時候會主動來獲取一次配置,同時,在節點上註冊一個Watcher,這樣一來,以後每次配置有更新的時候,都會實時通知到訂閱的客戶端,從來達到獲取最新配置信息的目的;
  2. 分佈式搜索服務中,索引的元信息和服務器集羣機器的節點狀態存放在ZK的一些指定節點,供各個客戶端訂閱使用;
  3. 分佈式日誌收集系統。這個系統的核心工作是收集分佈在不同機器的日誌。收集器通常是按照應用來分配收集任務單元,因此需要在ZK上創建一個以應用名作爲path的節點P,並將這個應用的所有機器ip,以子節點的形式註冊到節點P上,這樣一來就能夠實現機器變動的時候,能夠實時通知到收集器調整任務分配。

負載均衡

這裏說的負載均衡是指軟負載均衡。在分佈式環境中,爲了保證高可用性,通常同一個應用或同一個服務的提供方都會部署多份,達到對等服務。而消費者就須要在這些對等的服務器中選擇一個來執行相關的業務邏輯,其中比較典型的是消息中間件中的生產者,消費者負載均衡。

消息中間件中發佈者和訂閱者的負載均衡,linkedin開源的KafkaMQ和阿里開源的metaq都是通過zookeeper來做到生產者、消費者的負載均衡。這裏以metaq爲例來說明。

生產者負載均衡:metaq發送消息的時候,生產者在發送消息的時候必須選擇一臺broker上的一個分區來發送消息,因此metaq在運行過程中,會把所有broker和對應的分區信息全部註冊到ZK指定節點上,默認的策略是一個依次輪詢的過程,生產者在通過ZK獲取分區列表之後,會按照brokerId和partition的順序排列組織成一個有序的分區列表,發送的時候按照從頭到尾循環往復的方式選擇一個分區來發送消息。

消費者負載均衡: 在消費過程中,一個消費者會消費一個或多個分區中的消息,但是一個分區只會由一個消費者來消費。MetaQ的消費策略是:

  • 每個分區針對同一個group只掛載一個消費者。
  • 如果同一個group的消費者數目大於分區數目,則多出來的消費者將不參與消費。
  • 如果同一個group的消費者數目小於分區數目,則有部分消費者需要額外承擔消費任務。
  • 在某個消費者故障或者重啓等情況下,其他消費者會感知到這一變化(通過zookeeper watch消費者列表),然後重新進行負載均衡,保證所有的分區都有消費者進行消費。

分佈式鎖

分佈式鎖是控制分佈式系統之間同步訪問共享資源的一種方式。如果不同系統或同一系統不同機器之間共享了同一資源,那訪問這些資源時通常需要一些互斥手段來保證一致性,這種情況下就需要用到分佈式鎖了。

使用關係型數據庫是一種簡單、廣泛的實現方案,但大多數大型分佈式系統中數據庫已經是性能瓶頸了,如果再給數據庫添加額外的鎖會更加不堪重負;另外,使用數據庫做分佈式鎖,當搶到鎖的機器掛掉的話如何釋放鎖也是個頭疼的問題。

接下來看下使用ZK如何實現排他鎖。排他鎖的核心是如何保證當前有且只有一個事務獲得鎖,並且鎖被釋放後所有等待獲取鎖的事務能夠被通知到。

在這裏插入圖片描述
如圖所示,在需要獲取排他鎖時,所有客戶端都會試圖在/exclusive_lock下創建臨時子節點/exclusive_lock/lock,最終只有一個客戶端能創建成功,該客戶端就獲取到了鎖。同時沒有獲取到鎖的客戶端需要到/exclusive_lock節點上註冊一個子節點變更的Watcher監聽,用於實時監聽lock節點的變更情況。

/exclusive_lock/lock是一個臨時節點,在一下兩種情況下都有可能釋放鎖:

  • 當獲取鎖的客戶端掛掉,ZK上的該節點會被刪除;
  • 正常執行完業務邏輯之後客戶端會主動將自己創建的臨時節點刪除;

無論在什麼情況下刪除了lock臨時節點ZK都會通知在/exclusive_lock節點上註冊了子節點變更Watcher監聽的客戶端,重新發起鎖的獲取。

分佈式隊列或Barrier

隊列方面,簡單地講有兩種,一種是常規的先進先出隊列,另一種是要等到隊列成員聚齊之後的才統一按序執行。對於第一種先進先出隊列,和分佈式鎖服務中的控制時序場景基本原理一致,這裏不再贅述。 第二種隊列其實是在FIFO隊列的基礎上作了一個增強,可以理解爲是分佈式Barrier,舉個栗子,在大規模分佈式並行計算的場景下,最終的合併計算需要基於很多並行計算的子結果來進行,即系統需要滿足特定的條件,一個隊列的元素必須都聚齊之後才能進行後續處理,否則一直等待。

看下如何用ZK來支持這種場景。在這裏插入圖片描述
通常可以給/queue賦值n(假設n=10),表示隊列或Task的大小,凡是其中一個子任務完成(就緒),那麼就去/queue下建立自己的臨時時序節點(CreateMode.EPHEMERAL_SEQUENTIAL),當 /queue 發現自己下面的子節點滿足指定個數,就可以進行下一步處理了。
執行步驟:

  • 調用獲取節點數據的api獲取/queue節點的內容:10;
  • 調用獲取子節點總數的api獲取/queue下的所有子節點,並且註冊對子節點變更的Watcher監聽;
  • 統計子節點個數,如果子節點個數小於10則繼續等待,否則打開屏障繼續處理;
  • 接收到Watcher通知後,重複步驟2;

ZooKeeper在HBase中的應用

HBase全稱Hadoop DataBase,是一個基於Hadoop文件系統設計、面向海量數據的高可靠性、高性能、面向列、可伸縮的分佈式存儲系統。在HBase向在線分佈式存儲方向發展過程中,開發者發現如果有RegionServer服務器掛掉時系統和客戶端都無法及時得知信息,服務難以快速遷移到其它RegionServer服務器上,問題原因是缺少相應的分佈式協調組件,於是後來ZooKeeper被加入到HBase的技術體系中。

目前ZooKeeper已經成爲HBase的核心組件,應用場景包括系統容錯、RootRegion管理、Region狀態管理、分佈式SplitLog任務管理和Replication管理,除此之外還包括HMaster選舉、Table的enable/disable狀態記錄及幾乎所有元數據的存儲等。

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