一文看懂 ZooKeeper ,面試再也不用背八股(文末送PDF)

Data跳動 發表於 2022-05-20
面試 ZooKeeper

本文將以如下內容為主線講解ZooKeeper中的學習重點,包括 ZooKeeper 中的角色、ZAB協議、資料模型、選舉機制、監聽器原理以及應用場景等。會對相關的面試題或開發中常見內容,進行重點講解。
在這裡插入圖片描述
接下來將帶領大家入門學習 ZooKeeper 系列的內容,力求通俗易懂,圖文並茂。

一、ZooKeeper 的工作機制

1. 什麼是ZooKeeper

ZooKeeper 是一個分散式協調服務,其設計的初衷是為分散式軟體提供一致性服務。其本質上,就是檔案系統+通知機制。

ZooKeeper 提供了一個類似 Linux 檔案系統的樹形結構,ZooKeeper 的每個節點既可以是目錄也可以是資料,並且 ZooKeeper 還提供了對每個節點的監控與通知機制。

2. ZooKeeper 的工作機制

ZooKeeper 採用的是主從模式,有主節點和從節點。從設計模式的角度,它是一個基於觀察者模式設計的分散式服務管理框架,負責儲存和管理大家都關心的資料,然後接受觀察者的註冊,一旦這些資料的狀態發生變化,ZooKeeper 就將負責通知已經註冊的那些觀察者做出相應的反應。

3. ZooKeeper 的角色及其關係

ZooKeeper 中的角色包括 Leader、Follower、Observer。Leader 是叢集主節點,主要負責管理叢集狀態和接收使用者的寫請求;Follower 是從節點,主要負責叢集選舉投票和接收使用者的讀請求;Observer 的功能與 Follower 類似,只是沒有投票權,主要用於分擔 Follower 的讀請求,降低叢集的負載。
在這裡插入圖片描述

a. Leader

一個執行中的 Zookeeper 叢集只有一個 Leader 服務,Leader 服務主要包括以下兩個指責:

  • 負責叢集資料的寫操作。所有寫操作必須要 Leader 完成之後,才可以將寫操作廣播到其他 Follower,並且只有超過半數節點(不包括 Observer)寫入成功後,這些寫請求才算寫成功;
  • 發起並維護各個 Follower 以及 Observer 之間的心跳,以監控叢集的執行狀態。

b. Follower
一個 Zookeeper 叢集可以有多個 Follower,Follower通過心跳與 Leader 保持連線。Follower 服務主要有以下兩個指責:

  • 負責叢集資料的讀操作。Follower 在接受到一個客戶端請求之後,會先判斷該請求是讀請求還是寫請求,若為讀請求,則 Follower 從本地節點上讀取資料並返回給客戶端;若為寫請求,則 Follower 會將寫請求轉發給 Leader 來處理。
  • 參與叢集中 Leader 的選舉。當 Leader 失效之後,Follower 需要在叢集選舉時進行投票;(後續會詳細講解選舉機制)

c. Observer

一個 Zookeeper 叢集可以有多個 Observer,Observer 的主要職責是負責叢集資料的讀操作,其功能同以上介紹的 Follower 的功能類似,主要的差別就是 Observer 沒有投票權。

4. 面試題:Follower 已經具備了 Observer 的所有功能,為什麼還要設計 Observer 角色 ?

這是因為 ZooKeeper 叢集在執行過程中要支援更多的客戶端併發的操作,就需要增加更多的服務例項,而過多的服務例項會使得叢集的投票階段變得複雜,選舉時間過長不利於叢集故障的快速恢復。
因此,ZooKeeper 引入了 Observer 角色,Observer 不參與投票,只負責接收客戶端來的讀請求,以及將寫請求轉發給 Leader 。加入更多的 Observer 節點,不僅提高了 ZooKeeper 叢集的吞吐量,保障了系統的穩定性。


二、ZooKeeper 中的 ZAB 協議

前面介紹了 ZooKeeper 的基本概念、角色、工作機制等內容,為了後續更好地學習 ZooKeeper 的原理,本節先給大家介紹一種分散式一致性協議——ZAB(ZooKeeper Atomic Broadcast,ZooKeeper 原子訊息廣播協議)。

1. 什麼是ZAB

在介紹 ZAB 之前,先給大家介紹下 ZooKeeper 的由來。

ZooKeeper 最早起源於雅虎研究院的一個研究小組,當時雅虎內部很多大型系統都需要依賴一個類似的系統來進行分散式協調,為了讓把精力集中在業務邏輯上,雅虎研發人員開發了一個通用的解決了單點問題的分散式協調框架。

ZooKeeper 名字的由來:
立項之初,考慮到很多專案都是使用動物的名字來命名的,雅虎的工程師希望給這個專案也取一個動物的名字。有人開玩笑地說:“在這樣下去,我們這兒就變成動物園了!”此話一出,大家紛紛表示就叫動物園管理員吧。

而 ZAB 協議最初也只是為雅虎內部那些高吞吐量、低延遲的分散式系統場景設計的,它並不是一種通用的分散式一致性演算法,而是專門為 ZooKeeper 設計的一種支援崩潰恢復的原子廣播協議,實現了一種主備模式的系統架構來保持叢集中各副本之間資料的一致性。

2. 兩個概念

1)當前叢集的週期號:Epoch

叢集中每次 Leader 的重新選舉都會產生一個新的週期號(也有叫年代號的,都一個意思),週期號的產生規則則是在上一個週期號上加1,這樣當之前的 Leader 奔潰恢復後會發現自己的週期號比當前的週期號小,說明此時叢集已經產生了一個新的 Leader,舊的 Leader 會再次以 Follower 的角色加入叢集。

2)ZAB 協議的事務編號:Zxid

Zxid 是一個64位的數字,其中低32位儲存的是一個簡單的單調遞增的計數器,針對客戶端的每個事務請求,計數器都會加1。高32位儲存的是 Leader 的週期號 Epoch。每次選舉產生一個新的 Leader 時,該 Leader 就會從當前伺服器的日誌中取最大事務的 Zxid,獲取其中高32位的 Epoch 值並加1,以此作為一個新的 Epoch,並會將低32位從0開始重新計數。

3. 兩種模式

1)叢集選主:恢復模式

當叢集 Leader 出現崩潰,或者由於網路原因導致 Leader 和過半的 Follower 失去聯絡,那麼叢集將開始選主,該過程為恢復模式。

Leader 的選舉機制不僅可以讓 Leader 節點知道自身被選舉為 Leader,同時還能需要讓叢集中其他節點也能快速感知到選舉的新的 Leader 節點。

2)資料同步:廣播模式

當 Leader 被選舉出來後,Leader 將最新的叢集狀態廣播給其他 Follower,該過程為廣播模式。在半數以上的 Follower 完成與 Leader 的狀態同步後,廣播模式結束。

4. ZAB 的實現過程

1)選舉階段

選舉階段的目的就是產生一個準 Leader,節點在一開始都處於選舉階段,只要有一個節點得到超過半數節點的票數,那麼它就可以當選準Leader,只有到達第三階段(同步階段)這個準Leader才會成為真正的Leader。
在這裡插入圖片描述

2)發現階段

在該階段中 Follower 和上一階段選舉出的準 Leader 進行通訊,同步 Follower 最近接收的事務提議。這一階段的目的是發現當前大多數節點接收的最新提議,並且準 Leader 生成新 的epoch,然後讓 Follower 接收,更新它們的acceptedEpoch。
在這裡插入圖片描述

如果節點1認為節點L是 Leader,那麼當節點1嘗試連線節點L時,如果連線遭到拒絕,則叢集將會重新進入選舉階段。

3)同步階段

同步階段主要是將 Leader 在前一階段獲得的最新提議資訊同步到叢集中所有的副本。並且只有當超過半數的節點都同步完成後,準 Leader 才會成為真正的 Leader 。Follower 只會接收 Zxid比自己 lastZxid 大的提議。
在這裡插入圖片描述
同步完成之後,叢集的選主操作才算完成,新的 Leader 將產生。

4)廣播階段

在該階段,ZooKeeper 叢集正式對外提供事務服務,這時 Leader 進行訊息廣播,將其上的狀態通知到其他 Follower。若後續有新的節點加入進來,則 Leader 會對新節點進行狀態同步。


三、資料模型與監聽器

ZooKeeper 可用於統一命名服務、配置管理、叢集管理、分散式通知協調、分散式鎖等場景,在這些應用場景中, ZooKeeper 內部是如何做到分散式資料一致性的呢?本節將給大家介紹下 ZooKeeper 內部是如何做到分散式資料一致性的。

ZooKeeper 使用了一個樹形結構的名稱空間來表示其資料結構,其檢視結構和標準的 Unix 檔案系統非常類似,但沒有引入傳統檔案系統中目錄和檔案等相關概念,而是將 ZooKeeper 樹中的每一個節點都稱之為一個 Znode。其資料結構如下圖所示。

在這裡插入圖片描述
類似檔案系統的目錄樹,ZooKeeper 樹中的每個節點都可以擁有子節點,而不同的是,每個 Znode 節點都儲存了資料資訊,同時也提供了對節點資訊的監控等操作。

1. Znode 的資料模型

Znode 是 ZooKeeper 中資料的最小單元,每個 Znode 都兼具檔案和目錄兩種特點,既能像檔案一樣儲存和維護資料,又可以由一系列使用斜槓(/)進行分割的方式作為路徑標識的一部分。每個 Znode 都有以下三部分組成。

  • Stat:狀態資訊,用於儲存該 Znode 的版本、許可權、時間戳等資訊;
  • Data:實際儲存的資料;
  • Children:對子節點的資訊描述;

需要特別說明的是,Znode 節點雖然可以儲存資料資訊,但它並不能像資料庫那樣儲存大量的資料,Znode 的設計初衷就是儲存分散式應用中的配置檔案、叢集狀態等後設資料資訊。

2. Znode 的控制訪問

1)ACL

ACL(Access Control List) 為訪問控制列表,應用程式會根據實際需求將使用者分為只讀、只寫以及讀寫使用者,每一個 Znode 節點都會有一個 ACL 用來約束不同的使用者對節點的訪問許可權。

2)原子操作

每一個 Znode 節點上都具有原子操作的特性,讀操作將獲取與節點相關的資料,寫操作將替換節點上的資料,上期文章中講到 ZooKeeper 都會為每一個事務請求分配一個全域性唯一的事務編號 Zxid,每一個 Zxid 就對應一次更新操作,並可以通過 Zxid 來識別出更新操作請求的全域性順序。

3. Znode 的節點型別

ZooKeeper 中的節點有兩種,分別為臨時節點和永久節點。節點的型別在建立時即被確定,並且不能改變。

1)臨時節點

臨時節點常被應用於心跳監控,舉個例子,設定過期時間為 30s,要求各個子節點對應的服務端每 5s 傳送一次心跳到 ZooKeeper 叢集,當服務端連續 30s 沒有向 ZooKeeper 彙報心跳資訊,也就是連續6次沒有收到心跳資訊,就認為該節點當機了,並將其從服務列表中移除。

臨時節點的生命週期依賴於建立它們的會話(Session)。一旦會話結束,臨時節點將被自動刪除,當然可以也可以手動刪除。

另外,ZooKeeper 的臨時節點不允許擁有子節點。

2)永久節點

永久節點的資料會一直儲存著,直到使用者呼叫介面將其資料刪除,該節點一般用於儲存一些永久性的配置資訊。

4. Znode 的監聽器機制( 面試重點

ZooKeeper 的每個節點上都有一個 Watcher 用於監控節點資料的變化,當節點狀態發生改變時(Znode 新增、刪除、修改)將會觸發 Wahcher 所對應的操作。在 Watcher 被觸發時,ZooKeeper 會向監控該節點的客戶端傳送一條通知說明節點的變化情況。
在這裡插入圖片描述
具體實現流程就是,客戶端向 ZooKeeper 伺服器註冊 Watcher 的同時,會將 Watcher 物件儲存在客戶端的 WatchManager 中。當 ZooKeeper 伺服器端觸發 Watcher 事件後,會向客戶端傳送通知,客戶端執行緒從 WatchManager 中取出對應的 Watcher 物件來執行回撥邏輯。


四、ZooKeeper 的選舉機制和流程

我們在本系列的第一期就介紹了 ZooKeeper 叢集中的三個伺服器角色:Leader、Follower 和 Observer。其中,Leader 選舉是 ZooKeeper 中最重要的技術之一,也是保證分散式資料一致性的關鍵所在。本期內容將重點講解 Leader 是如何被選舉的。

1. 選舉機制概述

Zookeeper 在配置檔案中並沒有指定 Master 和 Slave。但是,Zookeeper 工作時, 是有一個節點為 Leader,其他則為 Follower,而這個 Leader 是通過內部的選舉機制臨時產生的。

每個 Server 首先都提議自己是 Leader,併為自己投票,然後將投票結果與其他 Server 的選票進行對比,權重大的勝出,使用權重較大的選票更新自身的投票箱,我們介紹下伺服器啟動時期的 Leader 選舉。

1)每一個 Server 都會發出一個投票

在叢集初次啟動時,每個 Server 都會推薦自己為 Leader,然後各自將這個投票發給叢集中其他 Server。

2)接收來自各個 Server 的投票

每個 Server 在接收到其他 Server 的投票後,首先會判斷該票的有效性,包括檢查是否本輪投票,是否來自 Looking 狀態的 Server。(Looking 狀態表示當前叢集正處於選舉狀態)

3)處理投票

針對每一個投票,Server 都會將別人的投票和自己的投票進行 PK,計算出 Zxid 最大的 Server,並將該 Server 設定成下一次投票推薦的 Server。

4)統計投票

每次投票結束之後,都會統計所有投票,獲取投票最多的 Server 將成為獲勝者,如果獲勝者的票數超過叢集個數的一半,則該 Server 將為推選為 Leader。否則繼續投票,直至 Leader 被選舉出來。

5)改變伺服器狀態

一旦 Leader 確定後,Leader 會通知其他 Follower 叢集已經成為 Uptodate 狀態,Follower 在收到 Uptodate 訊息後,接收 Client 的請求並開始對外提供服務。

2. 選舉 Leader 的具體例項

上述的選舉過程比較抽象,我們以一個有5個節點的叢集為例,目前均為 shutdown 狀態。

在這裡插入圖片描述
按照 Server 的編號依次啟動,看下整個的選舉過程是如何實現的。

1)Server 1 啟動

在這裡插入圖片描述
Server 1 啟動後會提議自己為 Leader 併為自己投票,然後將投票結果傳送給其他 Server,由於其他 Server 還未啟動,因此收不到任何反饋資訊,此時 Server 1 會處於 Looking 狀態。

2)Server 2 啟動
在這裡插入圖片描述

Server 2 啟動後會提議自己為 Leader 併為自己投票,然後與 Server 1 交換投票結果,由於 Server 2 的編號大於 Server 1,因此 Server 2 勝出。但是,由於投票數未過叢集數的一半,兩個 Server 仍然均處於 Looking 狀態。

3)Server 3 啟動
在這裡插入圖片描述
Server 3 啟動後會提議自己為 Leader 併為自己投票,然後與 Server 2、Server 3 交換投票結果,由於 Server 3 的編號最大,因此 Server 3 勝出。此時, Server 3 的票數大於叢集數的一半了,因此 Server 3 會更新為 Leader ,Server 1、Server 2 更新為 Follower。

4)Server 4 啟動
在這裡插入圖片描述
Server 4 啟動後會提議自己為 Leader 併為自己投票,然後與 Server 1、Server 2、Server 3 交換投票結果,發現 Server 3 已經成為了 Leader,因此 Server 4 也成為了 Follower。

5)Server 5 啟動
在這裡插入圖片描述

與 Server 4 一樣,Server 5 啟動後會給自己投票,然後與其他 Server 交換資訊,發現 Server 3 已經成為了 Leader,因此 Server 5 也成為了 Follower。

投票 vote 的資料結構

  • id:伺服器ID,用來唯一標識 ZooKeeper 叢集中的伺服器;
  • Zxid:事務ID,用來唯一標識一次伺服器狀態的變更;
  • electionEpoch:代表當前伺服器的選舉輪次,是一個自增序列;
  • peerEpoch:被推舉的 Leader 的選舉輪次;
  • state:當前伺服器的狀態。