一篇文章帶你深入理解Zookeeper

作者:Hollis;
來源:Hollis


一篇文章帶你深入理解Zookeeper

隨著互聯網技術的發展,大型網站需要的計算能力和存儲能力越來越高。網站架構逐漸從集中式轉變成分佈式。

雖然分佈式和集中式系統相比有很多優勢,比如能提供更強的計算、存儲能力,避免單點故障等問題。但是由於採用分佈式部署的方式,就經常會出現網絡故障等問題,並且如何在分佈式系統中保證數據的一致性和可用性也是一個比較關鍵的問題。

分佈式的工作方式有點類似於團隊合作。當有一項任務分配到某個團隊之後,團隊內部的成員開始各司其職,然後把工作結果統一彙總給團隊主管,由團隊主管再整理團隊的工作成果彙報給公司。

但是,日常工作中,如果兩個員工或用戶對某件事產生了分歧,通常我們的做法是找上級,去做數據和信息的同步。

那麼對於我們的服務呢,多個節點之間數據不同步如何處理?

對於分佈式集群來說,這個時候,我們通常一個能夠在各個服務或節點之間進行

協調服務或中間人

架構設計中,沒有一個問題不能通過增加一個抽象層來解決的,如果有,那就增加兩層。

一篇文章帶你深入理解Zookeeper

集群管理

我們可以一起看看,協調服務中的佼佼者--ZooKeeper

zookeeper起源

一篇文章帶你深入理解Zookeeper

一篇文章帶你深入理解Zookeeper

最初,在Hadoop生態中,會存在很多的服務或組件(比如hive、pig等),每個服務或組件之間進行協調處理是很麻煩的一件事情,急需一種高可用高性能數據強一致性的協調框架。

因此雅虎的工程師們創造了這個中間程序,但中間程序的命名卻愁死了開發人員,突然想到hadoop中的大多是動物名字,似乎缺乏一個管理員,這個程序的功能有是如此的相似。因此zookeeper誕生。

Zookeeper是一個開放源碼的分佈式服務協調組件,是Google Chubby的開源實現。是一個高性能的分佈式數據一致性解決方案。他將那些複雜的、容易出錯的分佈式一致性服務封裝起來,構成一個高效可靠的原語集,並提供一系列簡單易用的接口給用戶使用。

zookeeper提供了哪些特性,以便於能夠很好的完成協調能力的處理呢?

功能與特性

一篇文章帶你深入理解Zookeeper

數據存儲

zookeeper提供了類似Linux文件系統一樣的數據結構。每一個節點對應一個Znode節點,每一個Znode節點都可以存儲1MB(默認)的數據。

客戶端對zk的操作就是對Znode節點的操作。

一篇文章帶你深入理解Zookeeper

zookeeper數據結構

  • Znode:包含ACL權限控制、修改/訪問時間、最後一次操作的事務Id(zxid)等等
  • 說有數據存儲在內存中,在內存中維護這麼一顆樹。
  • 每次對Znode節點修改都是保證順序和原子性的操作。寫操作是原子性操作。

舉個例子,在註冊中心中,可以通過路徑"/fsof/服務名1/providers"找到"服務1"的所有提供者。

每一個Znode節點又根據節點的生命週期類型分為4種節點。

一篇文章帶你深入理解Zookeeper

  • 生命週期:當客戶端會話結束的時候,是否清理掉這個會話創建的節點。持久-不清理,臨時-清理。
  • 類型:每一個會話,創建單獨的節點(例子:正常節點:rudytan,順序編號節點:rudytan001,rudytan002等等)


監聽機制

zookeeper除了提供對Znode節點的處理能力,還提供了對節點的變更進行監聽通知的能力。

一篇文章帶你深入理解Zookeeper

監聽機制的步驟如下:

  1. 任何session(session1,session2)都可以對自己感興趣的znode監聽。
  2. 當znode通過session1對節點進行了修改。
  3. session1,session2都會收到znode的變更事件通知。

節點常見的事件通知有:

  • session建立成功事件
  • 節點添加
  • 節點刪除
  • 節點變更
  • 子節點列表變化

需要特別說明的是:

一次監聽事件,只會被觸發一次,如果想要監聽到znode的第二次變更,需要重新註冊監聽。

到這裡,我們瞭解到zookeeper提供的能力,那我們在哪些場景可以使用它?如何使用它呢?

應用場景

一篇文章帶你深入理解Zookeeper

zookeeper用得比較多的地方可能是,微服務的集群管理與服務註冊與發現。

註冊中心

一篇文章帶你深入理解Zookeeper

  • 依賴於臨時節點
  • 消費者啟動的時候,會先去註冊中心中全量拉取服務的註冊列表。
  • 當某個服務節點有變化的時候,通過監聽機制做數據更新。
  • zookeeper掛了,不影響消費者的服務調用。

目前還有個比較流行的服務Eureka也可以做註冊中心,他們有什麼優勢和劣勢呢?

註冊中心的對比

一篇文章帶你深入理解Zookeeper

通過上面的架構圖,可以發現Eureka不同於zk中的節點,Eureka中的節點每一個節點對等。是個AP系統,而不是zk的CP系統。在註冊中心的應用場景下,相對於與強數據一致性,更加關心可用性。

分佈式鎖

一篇文章帶你深入理解Zookeeper

  • 依賴於
    臨時順序節點
  • 判斷當前client的順序號是否是最小的,如果是獲取到鎖。
  • 沒有獲取到鎖的節點監聽最小節點的刪除事件(比如lock_key_001)
  • 鎖釋放,最小節點刪除,剩餘節點重新開始獲取鎖。
  • 重複步驟二到四。

redis和db也能創建分佈式鎖,那有什麼異同呢?

分佈式鎖的對比

具體差異比較:

從理解的難易程度角度(從低到高)

數據庫 > 緩存(Redis) > Zookeeper

從實現的複雜性角度(從低到高)

Zookeeper >= 緩存(Redis) > 數據庫

從性能角度(從高到低)

緩存(Redis) > Zookeeper >= 數據庫

從可靠性角度(從高到低)

Zookeeper > 緩存(Redis) > 數據庫

集群管理與master選舉

一篇文章帶你深入理解Zookeeper

  • 依賴於臨時節點
  • zookeeper保證無法重複創建一個已存在的數據節點,創建成功的client為master。
  • 非master,在已經創建的節點上註冊節點刪除事件監聽。
  • 當master掛掉後,其他集群節點收到節點刪除事件,進行重新選舉
  • 重複步驟二到四

當然還有其他應用場景,不一一列舉了。

有人說,zookeeper可以做分佈式配置中心、分佈式消息隊列,看到這裡的小夥伴們,你們覺得合適麼?

到這裡,可以基本上滿足基於zk應用開發的理論知識儲備。

高性能高可用強一致性保障

一篇文章帶你深入理解Zookeeper

高性能-分佈式集群

一篇文章帶你深入理解Zookeeper

高性能,我們通常想到的是通過集群部署來突破單機的性能瓶頸。對於zk來說,就是通過部署多個節點共同對外提供服務,來提供讀的高性能。

  • Master/Slave模式。
  • 在zookeeper中部署多臺節點對外提供服務,客戶端可以連接到任意一個節點。
  • 每個節點的數據都是一樣的。
  • 節點根據角色分為Leader節點與Learner節點(包括Follower節點與Observer節點)。
  • 集群中,只有一個Leader節點,完成所有的寫請求處理。
  • 每次寫請求都會生成一個全局的唯一的64位整型的事務ID(可以理解為全局的數據的版本號)。
  • Learner節點可以有很多,每個Leaner可以獨自處理讀請求,轉寫請求到Leader節點。
  • 當Leader節點掛掉後,會從Follower節點中通過選舉方式選出一個Leader提供對外服務。
  • Follower節點與Observer節點區別在於不參與選舉和提議的事務過半處理。
  • 集群通常是按照奇數個節點進行部署(偶然太對容災沒啥影響,浪費機器)。

數據一致性(zab協議-原子廣播協議)

通過集群的部署,根據CAP原理,這樣,可能導致同一個數據在不同節點上的數據不一致。zookeeper通過zab原子廣播協議來保證數據在每一個節點上的一致性。原子廣播協議(類似2PC提交協議)大概分為3個步驟。

一篇文章帶你深入理解Zookeeper

  • Leader包裝寫請求,生成唯一zxid,發起提議,廣播給所有Follower。
  • Follower收到提議後,寫入本地事務日誌,根據自身情況,是否同意該事務的提交。
  • Leader收到過半的Follower同意,自己先添加事務。然後對所有的Learner節點發送提交事務請求。

需要說明的是,zookeeper對數據一致性的要求是:

  • 順序一致性:嚴格按照事務發起的順序執行寫操作。
  • 原子性:所有事務請求的結果在集群中的所有節點上的應用情況是一致的。
  • 單一視圖:客戶端訪問任何一個節點,看到的數據模型都是一致的。
  • 實時性:保證在極小一段時間客戶端最終可以從服務讀取最新數據狀態(如果要實時,需要客戶端調用syn方法)。

可用性-leader選舉(zab協議-崩潰恢復協議)

在整個集群中,寫請求都集中在一個Leader節點上,如果Leader節點掛了咋辦呢?

一篇文章帶你深入理解Zookeeper

當集群初始化或Follower無法聯繫上Leader節點的時候,每個Follower開始進入選舉模式。選舉步驟如下:

  1. Follower節點第一次投票先投自己,然後將自己的選票廣播給剩餘的Follower節點。
  2. Follower節點接收到其他的選票。
  3. 選票比較:比較自己的與接收的選票的投票更有。
  4. 如果資金的選票不是最優選票,變更自己的選票,投最優選票的節點。
  5. 統計自己收到的選票,如果某個節點獲得了過半的節點的投票。確認該節點為新的Leader節點。
  6. 確認Leader節點後,每個節點變更自己的角色。完成投票選舉。

選舉原則:誰的數據最新,誰就有優先被選為Leader的資格。

舉個例子,假如現在zk集群有5個節點,然後掛掉了2個節點。剩餘節點S3,S4,S6開始進行選舉,他們的最大事務ID分別是6,2,6。定義投票結構為(投票的節點ID,被投節點ID,被投節點最大事務ID)。

一篇文章帶你深入理解Zookeeper

  1. 初始狀態,S3,S4,S5分別投自己,並帶上自己的最大事務ID。
  2. S3,S4,S5分別對自己收到的2票與自己的1票做比較。
  3. S5發現自己的是最優投票,不變更投票,S3,S4發現S5的投票是最優解,更改投票。
  4. S3,S4廣播自己變更的投票。
  5. 最後大家都確認了S5是Leader,S5節點狀態變更為Leader節點,S3,S4變更為Follower節點。

到這裡,就是選舉的主要過程。

數據的持久化

一篇文章帶你深入理解Zookeeper

  • zookeeper所有數據都存在內存中。
  • zookeeper會定期將內存dump到磁盤中,形成數據快照
  • zookeeper每次的事務請求,都會先接入到磁盤中,形成
    事務日誌
  • 全量數據 = 數據快照 + 事務日誌。

Zookeeper和CAP的關係

一篇文章帶你深入理解Zookeeper

前面提到了zk在可用性、數據一致性、性能等方面都表現的很優秀,也介紹了其中的原理。

但是分佈式系統的CAP理論告訴我們:任何軟件系統都無法同時滿足一致性、可用性以及分區容錯性。

那麼,Zookeeper其實也是一個分佈式系統,那麼也就要滿足CAP理論,也就是說,雖然在各個方面,ZK可以說是做了很多努力,但是在極端情況下,Zookeeper也需要在這三者之間有一些權衡,那麼Zookeeper在CAP中是如何取捨的呢?

ZooKeeper是個CP(一致性+分區容錯性)的,即任何時刻對ZooKeeper的訪問請求能得到一致的數據結果,同時系統對網絡分割具備容錯性,但是它不能保證每次服務請求的可用性(注:也就是在極端環境下,ZooKeeper可能會丟棄一些請求,消費者程序需要重新請求才能獲得結果)。

但是別忘了,ZooKeeper是分佈式協調服務,它的職責是保證數據(注:配置數據,狀態數據)在其管轄下的所有服務之間保持同步、一致,所以就不難理解為什麼ZooKeeper被設計成CP而不是AP特性的了。

如果是AP的,那麼將會帶來恐怖的後果(注:ZooKeeper就像交叉路口的信號燈一樣,你能想象在交通要道突然信號燈失靈的情況嗎?)。

而且, 作為ZooKeeper的核心實現算法 Zab,就是解決了分佈式系統下數據如何在多個服務之間保持同步問題的。

如果 ZooKeeper下所有節點都斷開了,或者集群中出現了網絡分割的故障(注:由於交換機故障導致交換機底下的子網間不能互訪)。

那麼ZooKeeper 會將它們都從自己管理範圍中剔除出去,外界就不能訪問到這些節點了,即便這些節點本身是“健康”的,可以正常提供服務的;所以導致到達這些節點的服務請求被丟失了。

那麼,再來深入原理看一下Zookeeper是如何在CAP之間做權衡的呢?

感悟

一篇文章帶你深入理解Zookeeper

最後,說說在整個學習和使用zk過程中的一個感悟吧。

  • 沒有銀彈,每一種技術或方案都有其優點和缺點。
  • 做一件事情很簡單,做好一件事件很難。


分享到:


相關文章: