zookeeper核心之ZAB協議就這麼簡單!

背景

咱們都知道 Zookeeper 是基於 ZAB 協議實現的,在介紹 ZAB 協議以前,先回顧一下 Zookeeper 的起源與發展。java

Zookeeper 到底是在什麼樣的時代背景下被提出?爲了解決了哪些棘手的問題?面試

Zookeeper 最先起源於雅虎研究院的一個研究小組。當時,研究人員發現,在雅虎的不少大型系統基本都須要依賴一個相似的系統來進行分佈式協調,可是這些系統都存在分佈式單點問題,因此雅虎的開發人員就試圖開發出一個通用的無單點問題的分佈式協調框架,以便讓開發人員將精力集中在處理業務邏輯上。算法

因而,Zookeeper 就誕生了!數據庫

Zookeeper 的出現不只解決了分佈式系統下數據一致性的問題,並且經歷過線上驗證,不管是從性能、易用性、穩定性上來講,都是工業級產品的標準。能夠說在分佈式系統中具備不可替代的核心地位,Hadoop、HBase、Storm 和 Solr 等大型分佈式系統都已經將 Zookeeper 做爲其核心組件,用於分佈式協調。即使 Zookeeper 如此優秀,可是 Zookeeper 依然是免費且開源的,全世界成千上萬的開發者都關注着它,陪同着一塊兒成長和發展。緩存

做爲一個開發者不管是爲了應付面試、晉升仍是我的技術成長的須要,都須要對 Zookeeper 有所瞭解,而學習 Zookeeper 的關鍵就是理解其核心部分: 服務器

ZAB 協議, 全稱(Zookeeper Atomic Broadcast)Zookeeper 原子消息廣播協議。微信

它與 Paxos 相似,也是一種數據一致性的算法。網絡

Zookeeper應該具有的特性

在 ZAB 協議的開發設計人員在協議設計之初並無要求 ZAB 具備很好的擴展性,最初只是爲了雅虎公司內部哪些高吞吐量、低延遲、健壯、簡單的分佈式系統場景設計的。基於 ZAB 協議,Zookeeper 實現了一種主備模式的系統架構來保持集羣中各副本之間數據的一致性,簡單架構圖以下:架構

Zookeeper 用一個單一的主進程來接收並處理客戶端的全部事物請求,並採用 ZAB 的原子廣播協議將服務器數據狀態以事物 Proposal 的形式廣播到全部的副本進程中去。這樣的模式就保證了,在同一時刻只有一個主進程來廣播服務器的狀態更變,所以可以很好地處理客戶端大量的併發請求,這在 ZAB 協議中叫:消息廣播。併發

除此以外,在分佈式環境中事物的執行順序也會存在必定的前後關係,好比:事務 C 的寫入須要依賴事務 B 的寫入,而事務 B 寫入須要依賴事務 A 寫入。這種先後依賴的順序也對 ZAB 協議提出了一個要求:ZAB 協議須要保證若是一個狀態的變動被處理了,那麼全部其依賴的狀態變動都已經被提早處理了。也就是須要順序執行。

另外除了能正常廣播消息、消息的順序執行,主進程也可能隨時會由於斷電、機器宕機等異常狀況沒法提供服務,所以,ZAB 協議還須要作到在當前主進程出現上述異常狀況的時候依然可以正常工做,這在 ZAB 協議中叫:崩潰恢復。

因此整個 ZAB 協議須要具有的核心特性已經被描述出來了,處理事務的請求的方式能夠簡單理解爲:

全部的事務請求必須由一個全局惟一的服務器來協調處理,這樣的服務器叫:Leader 服務器。其餘的服務器被稱爲 Follower 服務器。Leader 服務器將客戶端事務請求轉化成一個事務 Prososal(提議),並將改 Proposal 分發給集羣中全部的 Follower 服務器。以後 Leader 服務器接收了正確的反饋後,那麼 Leader 就會再次向全部的 Follower 服務器分發 Commit 消息,要求將前一個 Proposal 提交。

這就簡單闡述了ZAB 協議中消息廣播模式的部份內容

ZAB協議的兩種模式

ZAB 協議的包括兩種模式:崩潰恢復消息廣播

既然有兩種模式,那 Zookeeper 集羣何時進入奔潰恢復模式?何時進入消息廣播模式呢?

在進入奔潰恢復模式時 Zookeeper 集羣會進行 Leader 選舉,通常有兩種狀況會發生選舉:

  • 當服務器啓動時期會進行 Leader 選舉。
  • 當服務器運行期 Leader 服務器的出現網絡中斷、奔潰退出、重啓等異常狀況,或者當集羣中半數的服務器與該 Leader 服務器沒法通訊時,進入崩潰恢復模式,開始 Leader 選舉。

選舉出 Leader 服務器後,會進入消息廣播模式,開始接收處理客戶端的請求,前文已經描述,這裏再也不贅述。

相關名詞概念

在深刻講解 ZAB 協議的兩個模式以前,先解釋 Zookeeper 的幾個相關概念,方便理解 ZAB 協議:

三種角色

在前面提到 Zookeeper 的集羣中的服務器有 Leader 和 Follower ,但實際在 ZAB 協議中 Zookeeper 有三種角色,分別是 Leader、Folower、Observer,它們的分工各有不一樣:

  • Leader :負責整個Zookeeper 集羣工做機制中的核心,主要工做有一下兩個:

    • 事務請求的惟一調度和處理者,保證集羣事務處理的順序性
    • 集羣內部各服務器的調度者
  • Follower :它是 Leader 的追隨者,其主要工做有三個:

    • 處理客戶端的非實物請求,轉發事務請求給 Leader 服務器
    • 參與事務請求 Proposal 的投票
    • 參與 Leader 選舉投票
  • Observer :是 zookeeper 自 3.3.0 開始引入的一個角色,它不參與事務請求 Proposal 的投票,也不參與 Leader 選舉投票,只提供非事務的服務(查詢),一般在不影響集羣事務處理能力的前提下提高集羣的非事務處理能力。
三種狀態

在知道了 Zookeeper 中有三種角色後,不經提問: Zookeeper 是如何知道本身目前是什麼角色的呢?

在 ZAB 協議中定義:經過自身的狀態來區分本身的角色的,在運行期間各個進程可能出現如下三種狀態之一:

  • LOOKING:處在這個狀態時,會進入 Leader 選舉狀態
  • FOLLOWER:Follower 服務器和 Leader 服務器保持同步時的狀態
  • LEADING:Leader 服務器做爲主進程領導者的狀態

在組成 ZAB 協議的全部進程啓動的時候,初始化狀態都是 LOOKING 狀態,此時進程組中不存在 Leader,選舉以後纔有,在進行選舉成功後,就進入消息廣播模式,此時 Zookeeper 集羣中的角色狀態就再也不是 LOOKING 狀態。

ZXID

前文咱們知道 zookeeper 消息有嚴格的因果關係,所以必須將每個事務請求按照前後順序來進行排序與處理。那 Zookeeper 是如何保持請求處理的順序的呢?其中很是關鍵的點就是 ZXID。

那 ZXID 到底是怎麼發揮做用的呢?

Leader 服務器在接收到事務請求後,會爲每一個事務請求生成對應的 Proposal 來進行廣播,而且在廣播事務 Proposal 以前,Leader 服務器會首先爲這個事務 Proposal 分配一個全局單調遞增的惟一 ID ,咱們稱之爲事務 ID(即 ZXID)。

ZXID 的設計也頗有特色,是一個全局有序的 64 位的數字,能夠分爲兩個部分:

  • 高 32 位是: epoch(紀元),表明着週期,每當選舉產生一個新的 Leader 服務器時就會取出其本地日誌中最大事務的 ZXID ,解析出 epoch(紀元)值操做加 1做爲新的 epoch ,並將低 32 位置零。
  • 低 32 位是: counter(計數器),它是一個簡單的單調遞增的計數器,針對客戶端的每一個事務請求都會進行加 1 操做;

這裏低 32 位 counter(計數器)單調遞增還好理解,高 32 位 epoch(紀元)每次選舉加 1 也許有些同窗就有疑問了,爲何 epoch(紀元)每次選須要舉加 1 ,它在整個 ZAB 協議中有什麼做用?

咱們知道每當選舉產生一個新的 Leader 服務器時生成一個新的 epoch(紀元)值,而在前文咱們知道,服務運行過程當中觸發選舉 Leader 的條件是:Leader 服務器的出現網絡中斷、奔潰退出、重啓等異常狀況,或者當集羣中半數的服務器與該 Leader 服務器沒法通訊時

這說明整個 Zookeeper 集羣此時處於一個異常的狀況下,而在發生異常前,消息廣播進行到哪一步驟咱們根本不知道,集羣中的其餘 Follower 節點從這種崩潰恢復狀態從新選舉出 Leader 後,若是老 Leader 又恢復了鏈接進入集羣。此時老 Leader 的 epoch 確定會小於新 Leader 的 epoch,這時就將老 Leader 變成 Follower,對新的 Leader 進行數據同步。即使這時老 Leader 對其餘的 Follower 節點發送了請求,Follower 節點也會比較 ZXID 的值,由於高 32 位加 1 了, Follower 的 epoch(紀元)大於老 Leader 的 epoch(紀元),因此 Follower 會忽略這個請求。

這像改朝換代同樣,前朝的劍不能斬本朝的官。

消息廣播模式

知道了這些名詞,和上文提到的零散的知識點,其實崩潰恢復模式和消息廣播模式的過程你們大體有所瞭解了。

先看看消息廣播模式吧!

消息廣播的模式的過程簡圖以下所示:

整個過程相似一個二階段提交的過程,但卻有所不一樣,ZAB 協議簡化了二階段提交模型,在超過半數的 Follower 服務器已經反饋 ACK 以後就開始提交事務 Prososal 了,無需等待全部服務器響應。

結合上圖,看看消息廣播的具體細節:

  • Leader 服務器接收到請求後在進行廣播事務 Proposal 以前會爲這個事務分配一個 ZXID,再進行廣播。
  • Leader 服務器會爲每一個 Follower 服務器都各自分配一個單獨的隊列,而後將須要廣播的事務 Proposal 依次放入這些隊列中去,並根據 FIFO 策略進行消息的發送。
  • 每一個Follower 服務器在接收到後都會將其以事務日誌的形式寫入到本地磁盤中,而且在成功寫入後返回 Leader 服務器一個 ACK 響應。
  • 當有超過半數的服務器 ACK 響應後,Leader 就會廣播一個 Commit 消息給全部的 Follower 服務器,Follower 接收到後就完成對事務的提交操做。

在畫一張詳細點的流程圖,更直觀:

這就完成了整個消息廣播了!

崩潰恢復模式

前文已經反覆提過崩潰恢復模式了,其實就是從新選舉出新的 Leader 服務器,選舉完成後 Follower 服務器在再去同步 Leader 的數據。

運行中的服務再次進行從新選舉,必定是出現某種異常,咱們知道在出現異常狀況以前 Leader 的消息廣播可能會處在任何一個階段,有可能客戶端的請求只是在 Leader 服務器上提出並未被提交,也可能請求已經被 Leader 服務器提交。

ZAB 協議對於不一樣階段的出現的數據不一致的狀況作了兼容,保證:

  • 已經在 Leader 服務器上提交的事務,最終會被全部服務器都提交
  • 只在 Leader 服務器上提出的事務,要丟棄

針對以上的兩個要求,在進行 Leader 選舉時,之須要選舉出集羣中 ZXID 最大的事務 Proposal 便可,這樣就能夠省去 Leader 服務器檢查 Proposal 的提交和丟棄工做了。由於 Leader 服務器的事務是最大的,一切以 Leader 服務器的數據爲標準便可。

ZXID 在集羣中其實並非惟一的,因此也有可能出現多 Follower 服務器 ZXID 相同的狀況,這時候就須要比較 Zookeeper 的 SID 值。什麼是 SID?SID 是一個數字,和 zookeeper 的 myid 一致,myid 就不要解釋了,安裝過 Zookeeper 的都知道,每臺服務器都須要配置一個這樣的文件,裏面只有一個數字,用來標識這臺服務器。由於每臺機器的 myid 配置都不同,因此集羣選舉的時不會出現相等的狀況。

選舉時,比較大小的源碼以下:

前面已經說過,出現選舉 Leader 可能會出現兩種狀況:

  • 服務啓動時期,發起選舉
  • 服務運行期間,出現異常,發起選舉

但不管是啓動期仍是運行期進行 Leader 選舉,其選舉過程都差不太多,我簡單畫個流程圖:

結合上圖,奔潰恢復模式下 Leader 選舉的過程細節以下:

  • 檢測節點處於 LOOKING 階段,開發選舉 Leader
  • 發起投票時有兩種狀況:

    • 在服務啓動的初始階段,每一個服務器都會投票給本身以(myid,zxid)的信息形式發送,那初始階段沒有 zxid 值,就會發送(myid,0)
    • 在服務器運行期間,每一個服務器上的 zxid 都有值,且 zxid 都不相同,因此就正常發送(myid,zxid)
  • 各節點收到信息後將收到的(myid,zxid)和本身的比較,比較的過程前面已經說過,這裏再也不贅述
  • 而後判斷是否有半數的機器投票選出 Leader,若是否,在進入新一輪投票,直到選出
  • 選出 Leader 後,其餘節點就變成 Follower 角色,並向 Leader 發送本身服務器的最大 zxid ,Leader 服務器收到後會和本身本地的提議緩存隊列進行比較,判斷使用那種策略進行同步(後面詳細說明同步的四種策略)
  • 當同步完成,集羣就能夠正常的處理請求了,就進入消息廣播模式了。

這就是崩潰恢復模式下選舉 Leader 的過程了!

下面再簡單介紹下數據同步的四種策略,這四種同步策略保證了Zookeeper 集羣中的數據一致性,也解決了前文提出的兩個問題,兼容了各類數據不一致的場景。

數據同步的四種策略

在數據同步以前,Leader 服務器會進行數據同步的初始化,首先會從 Zookeeper 的內存數據庫中提取出事務前期對應的提議緩存隊列,同時會初始化三個 ZXID 的值:

  • peerLastZxid:這是 Follower 的最後處理 ZXID
  • minCommittedLog:Leader 服務器的提議緩存隊列中 最小的 ZXID
  • maxCommittedLog:Leader 服務器的提議緩存隊列中 最大的 ZXID

根據這三個參數,就能夠肯定四種同步方式,分別爲:

  • 直接差別化同步

    • 場景:當 minCommittedLog < peerLastZxid < maxCommittedLog 時
  • 先回滾在差別化同步

    • 場景:假如集羣有 A、B、C 三臺機器,此時 A 是 Leader 可是 A 掛了,在掛以前 A 生成了一個提議假設是:03,而後集羣有從新選舉 B 爲新的 Leader,此時生成的的提議緩存隊列爲:01~02,B 和 C 進行同步以後,生成新的紀元,ZXID 從 10 開始計數,集羣進入廣播模式處理了部分請求,假設如今 ZXID 執行到 15 這個值,此時 A 恢復了加入集羣,這時候就比較 A 最後提交的 ZXID:peerLastZxid 與 minCommittedLog、maxCommittedLog 的關係。此時雖然符合直接差別化同步:minCommittedLog < peerLastZxid < maxCommittedLog 這樣的關係,可是提議緩存隊列中卻沒有這個 ZXID ,這時候就須要先回滾,在進行同步。
  • 僅回滾同步

    • 場景:這裏和先回滾在差別化同步相似,直接回滾就能夠。
  • 全量同步

    • 場景:peerLastZxid < minCommittedLog,當遠遠落後 Leader 的數據時,直接全量同步。

這就是四種同步策略,這幾種同步方式也解決了上文提出的問題:

  • 只在 Leader 服務器上提出的事務,要丟棄(這個問題會在同步時,會進行回滾,使得只在 Leader 服務器上提出的事務丟棄)

這些就是整個 ZAB 協議中崩潰恢復的內容。

ZAB協議和Paxos算法的區別

ZAB協議看起來和Paxos有着相同之處,但它並非Paxos的典型實現,其實仍是有一些區別,ZAB協議中額外添加了一個同步的階段,二者設計目標也不太同樣,ZAB協議主要用於構建一個高可用的分佈式數據主備系統,而Paxos算法則是用於構建一個分佈式一致性的狀態機。

總結

Zookeeper 做爲出色的分佈式協調服務,目前讀 QPS 達到 12w,出色的性能也讓開發者更加青睞,其 ZAB 協議的核心分爲兩個部分:崩潰恢復、消息廣播。

典型的應用場景有:

  • 數據發佈/訂閱、負載均衡
  • 命名服務
  • 分佈式協調通知
  • 集羣管理
  • Master選舉
  • 分佈式鎖
  • 分佈式隊列
  • 用 Zookeeper 避免腦裂

除此以外在大數據領域也有應用,例如:

  • Hadoop
  • HBase
  • Kafka

在阿里巴巴集團內部實踐的 Zookeeper 的產品也有不少,如:

  • 消息中間件:Metamorphosis
  • RPC 服務框架:Dubbo
  • 基於 MySQL Binlog 的增量訂閱和消費組件:Cancel
  • 分佈式數據庫同步系統:Otter
  • 實時計算引擎:JStorm

zookeeper核心--ZAB協議

背景

咱們都知道 Zookeeper 是基於 ZAB 協議實現的,在介紹 ZAB 協議以前,先回顧一下 Zookeeper 的起源與發展。
Zookeeper 到底是在什麼樣的時代背景下被提出?爲了解決了哪些棘手的問題?
Zookeeper 最先起源於雅虎研究院的一個研究小組。當時,研究人員發現,在雅虎的不少大型系統基本都須要依賴一個相似的系統來進行分佈式協調,可是這些系統都存在分佈式單點問題,因此雅虎的開發人員就試圖開發出一個通用的無單點問題的分佈式協調框架,以便讓開發人員將精力集中在處理業務邏輯上。
因而,Zookeeper 就誕生了!
Zookeeper 的出現不只解決了分佈式系統下數據一致性的問題,並且經歷過線上驗證,不管是從性能、易用性、穩定性上來講,都是工業級產品的標準。能夠說在分佈式系統中具備不可替代的核心地位,Hadoop、HBase、Storm 和 Solr 等大型分佈式系統都已經將 Zookeeper 做爲其核心組件,用於分佈式協調。即使 Zookeeper 如此優秀,可是 Zookeeper 依然是免費且開源的,全世界成千上萬的開發者都關注着它,陪同着一塊兒成長和發展。
做爲一個開發者不管是爲了應付面試、晉升仍是我的技術成長的須要,都須要對 Zookeeper 有所瞭解,而學習 Zookeeper 的關鍵就是理解其核心部分:
ZAB 協議, 全稱(Zookeeper Atomic Broadcast)Zookeeper 原子消息廣播協議。
它與 Paxos 相似,也是一種數據一致性的算法。

Zookeeper應該具有的特性

在 ZAB 協議的開發設計人員在協議設計之初並無要求 ZAB 具備很好的擴展性,最初只是爲了雅虎公司內部哪些高吞吐量、低延遲、健壯、簡單的分佈式系統場景設計的。基於 ZAB 協議,Zookeeper 實現了一種主備模式的系統架構來保持集羣中各副本之間數據的一致性,簡單架構圖以下:
Zookeeper 用一個單一的主進程來接收並處理客戶端的全部事物請求,並採用 ZAB 的原子廣播協議將服務器數據狀態以事物 Proposal 的形式廣播到全部的副本進程中去。這樣的模式就保證了,在同一時刻只有一個主進程來廣播服務器的狀態更變,所以可以很好地處理客戶端大量的併發請求,這在 ZAB 協議中叫:消息廣播。
除此以外,在分佈式環境中事物的執行順序也會存在必定的前後關係,好比:事務 C 的寫入須要依賴事務 B 的寫入,而事務 B 寫入須要依賴事務 A 寫入。這種先後依賴的順序也對 ZAB 協議提出了一個要求:ZAB 協議須要保證若是一個狀態的變動被處理了,那麼全部其依賴的狀態變動都已經被提早處理了。也就是須要順序執行。
另外除了能正常廣播消息、消息的順序執行,主進程也可能隨時會由於斷電、機器宕機等異常狀況沒法提供服務,所以,ZAB 協議還須要作到在當前主進程出現上述異常狀況的時候依然可以正常工做,這在 ZAB 協議中叫:崩潰恢復。
因此整個 ZAB 協議須要具有的核心特性已經被描述出來了,處理事務的請求的方式能夠簡單理解爲:

全部的事務請求必須由一個全局惟一的服務器來協調處理,這樣的服務器叫:Leader 服務器。其餘的服務器被稱爲 Follower 服務器。Leader 服務器將客戶端事務請求轉化成一個事務 Prososal(提議),並將改 Proposal 分發給集羣中全部的 Follower 服務器。以後 Leader 服務器接收了正確的反饋後,那麼 Leader 就會再次向全部的 Follower 服務器分發 Commit 消息,要求將前一個 Proposal 提交。
這就簡單闡述了 ZAB 協議中消息廣播模式的部份內容

ZAB協議的兩種模式

ZAB 協議的包括兩種模式:崩潰恢復消息廣播
既然有兩種模式,那 Zookeeper 集羣何時進入奔潰恢復模式?何時進入消息廣播模式呢?
在進入奔潰恢復模式時 Zookeeper 集羣會進行 Leader 選舉,通常有兩種狀況會發生選舉:

  • 當服務器啓動時期會進行 Leader 選舉。
  • 當服務器運行期 Leader 服務器的出現網絡中斷、奔潰退出、重啓等異常狀況,或者當集羣中半數的服務器與該 Leader 服務器沒法通訊時,進入崩潰恢復模式,開始 Leader 選舉。

選舉出 Leader 服務器後,會進入消息廣播模式,開始接收處理客戶端的請求,前文已經描述,這裏再也不贅述。

相關名詞概念

在深刻講解 ZAB 協議的兩個模式以前,先解釋 Zookeeper 的幾個相關概念,方便理解 ZAB 協議:

三種角色

在前面提到 Zookeeper 的集羣中的服務器有 Leader 和 Follower ,但實際在 ZAB 協議中 Zookeeper 有三種角色,分別是 Leader、Folower、Observer,它們的分工各有不一樣:

  • Leader :負責整個Zookeeper 集羣工做機制中的核心,主要工做有一下兩個:

    • 事務請求的惟一調度和處理者,保證集羣事務處理的順序性
    • 集羣內部各服務器的調度者
  • Follower :它是 Leader 的追隨者,其主要工做有三個:

    • 處理客戶端的非實物請求,轉發事務請求給 Leader 服務器
    • 參與事務請求 Proposal 的投票
    • 參與 Leader 選舉投票
  • Observer :是 zookeeper 自 3.3.0 開始引入的一個角色,它不參與事務請求 Proposal 的投票,也不參與 Leader 選舉投票,只提供非事務的服務(查詢),一般在不影響集羣事務處理能力的前提下提高集羣的非事務處理能力。
三種狀態

在知道了 Zookeeper 中有三種角色後,不經提問: Zookeeper 是如何知道本身目前是什麼角色的呢?
在 ZAB 協議中定義:經過自身的狀態來區分本身的角色的,在運行期間各個進程可能出現如下三種狀態之一:

  • LOOKING:處在這個狀態時,會進入 Leader 選舉狀態
  • FOLLOWER:Follower 服務器和 Leader 服務器保持同步時的狀態
  • LEADING:Leader 服務器做爲主進程領導者的狀態

在組成 ZAB 協議的全部進程啓動的時候,初始化狀態都是 LOOKING 狀態,此時進程組中不存在 Leader,選舉以後纔有,在進行選舉成功後,就進入消息廣播模式,此時 Zookeeper 集羣中的角色狀態就再也不是 LOOKING 狀態。

ZXID

前文咱們知道 zookeeper 消息有嚴格的因果關係,所以必須將每個事務請求按照前後順序來進行排序與處理。那 Zookeeper 是如何保持請求處理的順序的呢?其中很是關鍵的點就是 ZXID。
那 ZXID 到底是怎麼發揮做用的呢?
Leader 服務器在接收到事務請求後,會爲每一個事務請求生成對應的 Proposal 來進行廣播,而且在廣播事務 Proposal 以前,Leader 服務器會首先爲這個事務 Proposal 分配一個全局單調遞增的惟一 ID ,咱們稱之爲事務 ID(即 ZXID)。
ZXID 的設計也頗有特色,是一個全局有序的 64 位的數字,能夠分爲兩個部分:

  • 高 32 位是: epoch(紀元),表明着週期,每當選舉產生一個新的 Leader 服務器時就會取出其本地日誌中最大事務的 ZXID ,解析出 epoch(紀元)值操做加 1做爲新的 epoch ,並將低 32 位置零。
  • 低 32 位是: counter(計數器),它是一個簡單的單調遞增的計數器,針對客戶端的每一個事務請求都會進行加 1 操做;

這裏低 32 位 counter(計數器)單調遞增還好理解,高 32 位 epoch(紀元)每次選舉加 1 也許有些同窗就有疑問了,爲何 epoch(紀元)每次選須要舉加 1 ,它在整個 ZAB 協議中有什麼做用?
咱們知道每當選舉產生一個新的 Leader 服務器時生成一個新的 epoch(紀元)值,而在前文咱們知道,服務運行過程當中觸發選舉 Leader 的條件是:Leader 服務器的出現網絡中斷、奔潰退出、重啓等異常狀況,或者當集羣中半數的服務器與該 Leader 服務器沒法通訊時
這說明整個 Zookeeper 集羣此時處於一個異常的狀況下,而在發生異常前,消息廣播進行到哪一步驟咱們根本不知道,集羣中的其餘 Follower 節點從這種崩潰恢復狀態從新選舉出 Leader 後,若是老 Leader 又恢復了鏈接進入集羣。此時老 Leader 的 epoch 確定會小於新 Leader 的 epoch,這時就將老 Leader 變成 Follower,對新的 Leader 進行數據同步。即使這時老 Leader 對其餘的 Follower 節點發送了請求,Follower 節點也會比較 ZXID 的值,由於高 32 位加 1 了, Follower 的 epoch(紀元)大於老 Leader 的 epoch(紀元),因此 Follower 會忽略這個請求。
這像改朝換代同樣,前朝的劍不能斬本朝的官。

消息廣播模式

知道了這些名詞,和上文提到的零散的知識點,其實崩潰恢復模式和消息廣播模式的過程你們大體有所瞭解了。
先看看消息廣播模式吧!
消息廣播的模式的過程簡圖以下所示:
<img src="https://upload-images.jianshu.io/upload_images/2710833-927c4264c545c4e7.png" alt="image.png" />
整個過程相似一個二階段提交的過程,但卻有所不一樣,ZAB 協議簡化了二階段提交模型,在超過半數的 Follower 服務器已經反饋 ACK 以後就開始提交事務 Prososal 了,無需等待全部服務器響應。
結合上圖,看看消息廣播的具體細節:

  • Leader 服務器接收到請求後在進行廣播事務 Proposal 以前會爲這個事務分配一個 ZXID,再進行廣播。
  • Leader 服務器會爲每一個 Follower 服務器都各自分配一個單獨的隊列,而後將須要廣播的事務 Proposal 依次放入這些隊列中去,並根據 FIFO 策略進行消息的發送。
  • 每一個Follower 服務器在接收到後都會將其以事務日誌的形式寫入到本地磁盤中,而且在成功寫入後返回 Leader 服務器一個 ACK 響應。
  • 當有超過半數的服務器 ACK 響應後,Leader 就會廣播一個 Commit 消息給全部的 Follower 服務器,Follower 接收到後就完成對事務的提交操做。

在畫一張詳細點的流程圖,更直觀:
<img src="https://upload-images.jianshu.io/upload_images/2710833-e53b8b881792203a.png" alt="image.png" />
這就完成了整個消息廣播了!

崩潰恢復模式

前文已經反覆提過崩潰恢復模式了,其實就是從新選舉出新的 Leader 服務器,選舉完成後 Follower 服務器在再去同步 Leader 的數據。
運行中的服務再次進行從新選舉,必定是出現某種異常,咱們知道在出現異常狀況以前 Leader 的消息廣播可能會處在任何一個階段,有可能客戶端的請求只是在 Leader 服務器上提出並未被提交,也可能請求已經被 Leader 服務器提交。
ZAB 協議對於不一樣階段的出現的數據不一致的狀況作了兼容,保證:

  • 已經在 Leader 服務器上提交的事務,最終會被全部服務器都提交
  • 只在 Leader 服務器上提出的事務,要丟棄

針對以上的兩個要求,在進行 Leader 選舉時,之須要選舉出集羣中 ZXID 最大的事務 Proposal 便可,這樣就能夠省去 Leader 服務器檢查 Proposal 的提交和丟棄工做了。由於 Leader 服務器的事務是最大的,一切以 Leader 服務器的數據爲標準便可。
ZXID 在集羣中其實並非惟一的,因此也有可能出現多 Follower 服務器 ZXID 相同的狀況,這時候就須要比較 Zookeeper 的 SID 值。什麼是 SID?SID 是一個數字,和 zookeeper 的 myid 一致,myid 就不要解釋了,安裝過 Zookeeper 的都知道,每臺服務器都須要配置一個這樣的文件,裏面只有一個數字,用來標識這臺服務器。由於每臺機器的 myid 配置都不同,因此集羣選舉的時不會出現相等的狀況。
選舉時,比較大小的源碼以下:
<img src="https://upload-images.jianshu.io/upload_images/2710833-bbcba5240c2a1db0.png" alt="image.png" />
前面已經說過,出現選舉 Leader 可能會出現兩種狀況:

  • 服務啓動時期,發起選舉
  • 服務運行期間,出現異常,發起選舉

但不管是啓動期仍是運行期進行 Leader 選舉,其選舉過程都差不太多,我簡單畫個流程圖:
<img src="https://upload-images.jianshu.io/upload_images/2710833-d87f4a39511a8630.png" alt="image.png" />
結合上圖,奔潰恢復模式下 Leader 選舉的過程細節以下:

  • 檢測節點處於 LOOKING 階段,開發選舉 Leader
  • 發起投票時有兩種狀況:

    • 在服務啓動的初始階段,每一個服務器都會投票給本身以(myid,zxid)的信息形式發送,那初始階段沒有 zxid 值,就會發送(myid,0)
    • 在服務器運行期間,每一個服務器上的 zxid 都有值,且 zxid 都不相同,因此就正常發送(myid,zxid)
  • 各節點收到信息後將收到的(myid,zxid)和本身的比較,比較的過程前面已經說過,這裏再也不贅述
  • 而後判斷是否有半數的機器投票選出 Leader,若是否,在進入新一輪投票,直到選出
  • 選出 Leader 後,其餘節點就變成 Follower 角色,並向 Leader 發送本身服務器的最大 zxid ,Leader 服務器收到後會和本身本地的提議緩存隊列進行比較,判斷使用那種策略進行同步(後面詳細說明同步的四種策略)
  • 當同步完成,集羣就能夠正常的處理請求了,就進入消息廣播模式了。

這就是崩潰恢復模式下選舉 Leader 的過程了!
下面再簡單介紹下數據同步的四種策略,這四種同步策略保證了Zookeeper 集羣中的數據一致性,也解決了前文提出的兩個問題,兼容了各類數據不一致的場景。

數據同步的四種策略

在數據同步以前,Leader 服務器會進行數據同步的初始化,首先會從 Zookeeper 的內存數據庫中提取出事務前期對應的提議緩存隊列,同時會初始化三個 ZXID 的值:

  • peerLastZxid:這是 Follower 的最後處理 ZXID
  • minCommittedLog:Leader 服務器的提議緩存隊列中 最小的 ZXID
  • maxCommittedLog:Leader 服務器的提議緩存隊列中 最大的 ZXID

根據這三個參數,就能夠肯定四種同步方式,分別爲:

  • 直接差別化同步

    • 場景:當 minCommittedLog < peerLastZxid < maxCommittedLog 時
  • 先回滾在差別化同步

    • 場景:假如集羣有 A、B、C 三臺機器,此時 A 是 Leader 可是 A 掛了,在掛以前 A 生成了一個提議假設是:03,而後集羣有從新選舉 B 爲新的 Leader,此時生成的的提議緩存隊列爲:01~02,B 和 C 進行同步以後,生成新的紀元,ZXID 從 10 開始計數,集羣進入廣播模式處理了部分請求,假設如今 ZXID 執行到 15 這個值,此時 A 恢復了加入集羣,這時候就比較 A 最後提交的 ZXID:peerLastZxid 與 minCommittedLog、maxCommittedLog 的關係。此時雖然符合直接差別化同步:minCommittedLog < peerLastZxid < maxCommittedLog 這樣的關係,可是提議緩存隊列中卻沒有這個 ZXID ,這時候就須要先回滾,在進行同步。
  • 僅回滾同步

    • 場景:這裏和先回滾在差別化同步相似,直接回滾就能夠。
  • 全量同步

    • 場景:peerLastZxid < minCommittedLog,當遠遠落後 Leader 的數據時,直接全量同步。

這就是四種同步策略,這幾種同步方式也解決了上文提出的問題:

  • 只在 Leader 服務器上提出的事務,要丟棄(這個問題會在同步時,會進行回滾,使得只在 Leader 服務器上提出的事務丟棄)

這些就是整個 ZAB 協議中崩潰恢復的內容。

ZAB協議和Paxos算法的區別

ZAB協議看起來和Paxos有着相同之處,但它並非Paxos的典型實現,其實仍是有一些區別,ZAB協議中額外添加了一個同步的階段,二者設計目標也不太同樣,ZAB協議主要用於構建一個高可用的分佈式數據主備系統,而Paxos算法則是用於構建一個分佈式一致性的狀態機。

總結

Zookeeper 做爲出色的分佈式協調服務,目前讀 QPS 達到 12w,出色的性能也讓開發者更加青睞,其 ZAB 協議的核心分爲兩個部分:崩潰恢復、消息廣播。
典型的應用場景有:

  • 數據發佈/訂閱、負載均衡
  • 命名服務
  • 分佈式協調通知
  • 集羣管理
  • Master選舉
  • 分佈式鎖
  • 分佈式隊列
  • 用 Zookeeper 避免腦裂

除此以外在大數據領域也有應用,例如:

  • Hadoop
  • HBase
  • Kafka

在阿里巴巴集團內部實踐的 Zookeeper 的產品也有不少,如:

  • 消息中間件:Metamorphosis
  • RPC 服務框架:Dubbo
  • 基於 MySQL Binlog 的增量訂閱和消費組件:Cancel
  • 分佈式數據庫同步系統:Otter
  • 實時計算引擎:JStorm

歡迎關注個人我的微信公衆號:java之旅

相關文章
相關標籤/搜索