萬字常人帶你深入理解Zookeeper!為你的春招做好準備!

Hi丶ImViper發表於2020-12-06

Table of Contents generated with DocToc

ZooKeeper 是什麼?

ZooKeeper 是一個開源的分散式協調服務。它是一個為分散式應用提供一致性服務的軟體,分散式應用程式可以基於 Zookeeper 實現諸如資料釋出/訂閱、負載均衡、命名服務、分散式協調/通知、叢集管理、Master 選舉、分散式鎖和分散式佇列等功能。

ZooKeeper 的目標就是封裝好複雜易出錯的關鍵服務,將簡單易用的介面和效能高效、功能穩定的系統提供給使用者。

Zookeeper 保證瞭如下分散式一致性特性:

  1. 順序一致性
  2. 原子性
  3. 單一檢視
  4. 可靠性
  5. 實時性(最終一致性)

客戶端的讀請求可以被叢集中的任意一臺機器處理,如果讀請求在節點上註冊了監聽器,這個監聽器也是由所連線的 zookeeper 機器來處理。對於寫請求,這些請求會同時發給其他 zookeeper 機器並且達成一致後,請求才會返回成功。因此,隨著 zookeeper 的叢集機器增多,讀請求的吞吐會提高但是寫請求的吞吐會下降。

有序性是 zookeeper 中非常重要的一個特性,所有的更新都是全域性有序的,每個更新都有一個唯一的時間戳,這個時間戳稱為 zxid(Zookeeper Transaction Id)。而讀請求只會相對於更新有序,也就是讀請求的返回結果中會帶有這個zookeeper 最新的 zxid。

ZooKeeper 設計目標

簡單的資料模型

ZooKeeper 允許分散式程式通過共享的層次結構名稱空間進行相互協調,這與標準檔案系統類似。 名稱空間由 ZooKeeper 中的資料暫存器組成 - 稱為znode,這些類似於檔案和目錄。 與為儲存設計的典型檔案系統不同,ZooKeeper資料儲存在記憶體中,這意味著ZooKeeper可以實現高吞吐量和低延遲。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-O6GbCAYV-1607193049314)(E:\學習\寫作\別人的文章\最近可抄\Java-Summarize-master\image/Zookeeper-1.png)]

可構建叢集

為了保證高可用,最好是以叢集形態來部署 ZooKeeper,這樣只要叢集中大部分機器是可用的(能夠容忍一定的機器故障),那麼zookeeper本身仍然是可用的。 客戶端在使用 ZooKeeper 時,需要知道叢集機器列表,通過與叢集中的某一臺機器建立 TCP 連線來使用服務,客戶端使用這個TCP連結來傳送請求、獲取結果、獲取監聽事件以及傳送心跳包。如果這個連線異常斷開了,客戶端可以連線到另外的機器上。

ZooKeeper 官方提供的架構圖:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-lXL5qt1p-1607193049316)(E:\學習\寫作\別人的文章\最近可抄\Java-Summarize-master\image/Zookeeper-2.png)]

上圖中每一個Server代表一個安裝Zookeeper服務的伺服器。組成 ZooKeeper 服務的伺服器都會在記憶體中維護當前的伺服器狀態,並且每臺伺服器之間都互相保持著通訊。叢集間通過 Zab 協議(Zookeeper Atomic Broadcast)來保持資料的一致性。

順序訪問

對於來自客戶端的每個更新請求,ZooKeeper 都會分配一個全域性唯一的遞增編號,這個編號反應了所有事務操作的先後順序,應用程式可以使用 ZooKeeper 這個特性來實現更高層次的同步原語。 這個編號也叫做時間戳——zxid(Zookeeper Transaction Id)

高效能

ZooKeeper 是高效能的。 在“讀”多於“寫”的應用程式中尤其地高效能,因為“寫”會導致所有的伺服器間同步狀態。(“讀”多於“寫”是協調服務的典型場景。)

ZooKeeper 提供了什麼?

  • 檔案系統
  • 通知機制

Zookeeper 檔案系統

Zookeeper 提供一個多層級的節點名稱空間(節點稱為 znode)。與檔案系統不同的是,這些節點都可以設定關聯的資料,而檔案系統中只有檔案節點可以存放資料而目錄節點不行。

Zookeeper 為了保證高吞吐和低延遲,在記憶體中維護了這個樹狀的目錄結構,這種特性使得 Zookeeper 不能用於存放大量的資料,每個節點的存放資料上限為1M。

Zookeeper 怎麼保證主從節點的狀態同步?

Zookeeper 的核心是原子廣播機制,這個機制保證了各個 server 之間的同步。實現這個機制的協議叫做 Zab 協議。Zab 協議有兩種模式,它們分別是恢復模式和廣播模式。

恢復模式

當服務啟動或者在領導者崩潰後,Zab就進入了恢復模式,當領導者被選舉出來,且大多數 server 完成了和 leader 的狀態同步以後,恢復模式就結束了。狀態同步保證了 leader 和 server 具有相同的系統狀態。

廣播模式

一旦 leader 已經和多數的 follower 進行了狀態同步後,它就可以開始廣播訊息了,即進入廣播狀態。這時候當一個 server 加入 ZooKeeper 服務中,它會在恢復模式下啟動,發現 leader,並和 leader 進行狀態同步。待到同步結束,它也參與訊息廣播。ZooKeeper 服務一直維持在 Broadcast 狀態,直到 leader 崩潰了或者 leader 失去了大部分的 followers 支援。

四種型別的資料節點 Znode

持久節點

除非手動刪除,否則節點一直存在於 Zookeeper 上

臨時節點

臨時節點的生命週期與客戶端會話繫結,一旦客戶端會話失效(客戶端與zookeeper 連線斷開不一定會話失效),那麼這個客戶端建立的所有臨時節點都會被移除。

持久順序節點

基本特性同持久節點,只是增加了順序屬性,節點名後邊會追加一個由父節點維護的自增整型數字。

臨時順序節點

基本特性同臨時節點,增加了順序屬性,節點名後邊會追加一個由父節點維護的自增整型數字。

Zookeeper 的典型應用場景

Zookeeper 是一個典型的釋出/訂閱模式的分散式資料管理與協調框架,開發人員可以使用它來進行分散式資料的釋出和訂閱。

通過對 Zookeeper 中豐富的資料節點進行交叉使用,配合 Watcher 事件通知機制,可以非常方便的構建一系列分散式應用中年都會涉及的核心功能,如:

  1. 資料釋出/訂閱
  2. 負載均衡
  3. 命名服務
  4. 分散式協調/通知
  5. 叢集管理
  6. Master 選舉
  7. 分散式鎖
  8. 分散式佇列

Zookeeper的通知機制

客戶端端會對某個 znode 建立一個 watcher 事件,當該 znode 發生變化時,這些客戶端會收到 zookeeper 的通知,然後客戶端可以根據 znode 變化來做出業務上的改變。

Zookeeper的分散式鎖實現方式

在講zk分佈鎖之前,先看下zookeeper中幾個關於節點的有趣的性質:

  1. 有序節點:假如當前有一個父節點為/lock,我們可以在這個父節點下面建立子節點;zookeeper提供了一個可選的有序特性,例如我們可以建立子節點“/lock/node-”並且指明有序,那麼zookeeper在生成子節點時會根據當前的子節點數量自動新增整數序號,也就是說如果是第一個建立的子節點,那麼生成的子節點為/lock/node-0000000000,下一個節點則為/lock/node-0000000001,依次類推。
  2. 臨時節點:客戶端可以建立一個臨時節點,在會話結束或者會話超時後,zookeeper會自動刪除該節點。
  3. 事件監聽:在讀取資料時,我們可以同時對節點設定事件監聽,當節點資料或結構變化時,zookeeper會通知客戶端。當前zookeeper有如下四種事件:
    1、節點建立;2、節點刪除;3、節點資料修改;4、子節點變更。

下面描述使用zookeeper實現分散式鎖的演算法流程,假設鎖空間的根節點為/lock:

  1. 客戶端連線zookeeper,並在/lock下建立臨時的且有序的子節點,第一個客戶端對應的子節點為/lock/lock-0000000000,第二個為/lock/lock-0000000001,以此類推。
  2. 客戶端獲取/lock下的子節點列表,判斷自己建立的子節點是否為當前子節點列表中序號最小的子節點,如果是則認為獲得鎖,否則監聽/lock的子節點變更訊息,獲得子節點變更通知後重復此步驟直至獲得鎖;
  3. 執行業務程式碼;
  4. 完成業務流程後,刪除對應的子節點釋放鎖。

Zookeeper 採用的哪種分散式一致性協議? 還有哪些分散式一致性協議

zab協議是zookeeper專門設計的支援崩潰恢復的原子廣播協議。目的是實現分散式zoopkeeper各個節點資料一致性。

zab協議約定zk節點有兩種角色leader和follower,zk客戶端會隨機的連結到 zookeeper 叢集中的一個節點,如果是讀請求,就直接從當前節點中讀取資料;如果是寫請求,那麼節點就會向 Leader 提交事務,Leader 接收到事務提交,會廣播該事務,只要超過半數節點寫入成功,該事務就會被提交。

ZAB協議包括兩種基本的模式:訊息廣播和崩潰恢復。

整個 Zookeeper 就是在這兩個模式之間切換。 簡而言之,當 Leader 服務可以正常使用,就進入訊息廣播模式,當 Leader 不可用時,則進入崩潰恢復模式。

以上其實大致經歷了三個步驟:

  1. 崩潰恢復:主要就是Leader選舉過程。
  2. 資料同步:Leader伺服器與其他伺服器進行資料同步。
  3. 訊息廣播:Leader伺服器將資料傳送給其他伺服器。

支援崩潰恢復後資料準確性的就是資料同步了,資料同步基於事務的 ZXID 的唯一性來保證。通過 + 1 操作可以辨別事務的先後順序。

ZAD和Paxos演算法的聯絡和區別

共同點:

  1. 兩者都存在一個類似於Leader程式的角色,由其負責協調多個Follow程式的執行。
  2. Leader程式都會等待超過半數的Follower做出正確的反饋後,才會將一個提案進行提交。
  3. 在ZAB協議中,每個Proposal中都包含了一個epoch值,用來代表當前Leader週期,在Paxos演算法中,同樣存在這樣一個標識,只是名字變成了Ballot。
    不同點:

Paxos演算法中,一個新的選舉產生的主程式會進行兩個階段的工作

  1. 讀階段,新的主程式會通過和所有其他程式進行通訊的方式來蒐集上一個主程式提出的提案,並將它們提交。
  2. 寫階段,當前主程式開始提出它自己的提案。
  3. ZAB在Paxos基礎上額外新增一個同步階段。同步階段之前,ZAB協議存在一個和Paxos讀階段類似的發現(Discovery)階段

同步階段中,新的Leader會確儲存在過半的Follower已經提交了之前Leader週期中的所有事務Proposal

  • 發現階段的存在,確保所有程式都已經完成對之前所有事物Proposal的提交
  • ZAB協議主要用於構建一個高可用的分散式資料主備系統,例如ZooKeeper,Paxos演算法則是用於構建一個分散式的一致性狀態機系統

Zookeeper 是怎樣保證主從節點的狀態同步

Zookeeper的核心是原子廣播,這個機制保證了各個Server之間的同步。實現這個機制的協議叫做Zab協議。Zab協議有兩種模式,它們分別是恢復模式(選主)和廣播模式(同步)。

恢復模式:當服務啟動或者在領導者崩潰後,Zab就進入了恢復模式,當領導者被選舉出來,且大多數Server完成了和leader的狀態同步以後,恢復模式就結束了。

因此,選主得到的leader保證了同步狀態的進行,狀態同步又保證了leader和Server具有相同的系統狀態,當leader失去主權後可以在其他follower中選主新的leader。

叢集中為什麼要有主節點

在分散式環境中,有些業務邏輯只需要叢集中的某一臺機器進行執行,

其他的機器可以共享這個結果,這樣可以大大減少重複計算,提高效能,所以就需要主節點

leader 選舉過程

有兩種情況會發起Leader選舉:

  1. 伺服器啟動的時候
  2. 伺服器執行的時候當Leader當機

在講解流程之前,先說明一下選舉流程中涉及到的角色:

  • LOOKING:尋找Leader狀態,處於該狀態需要進入選舉流程(只有該節點才可以投票)
  • LEADING:領導者狀態,處於該狀態的節點說明是角色已經是Leader
  • FOLLOWING:跟隨者狀態,表示Leader已經選舉出來,當前節點角色是follower
  • OBSERVER:觀察者狀態,表明當前節點角色是observer(該節點不參與競選)

三個核心選舉原則:

  1. Zookeeper叢集中只有超過半數以上的伺服器啟動,叢集才能正常工作;
  2. 在叢集正常工作之前,myid小的伺服器給myid大的伺服器投票,直到叢集正常工作,選出Leader;
  3. 選出Leader之後,之前的伺服器狀態由Looking改變為Following,以後的伺服器都是Follower。

下面以一個簡單的例子來說明整個選舉的過程:

假設有五臺伺服器組成的Zookeeper叢集,它們的id從1-5,同時它們都是最新啟動的,也就是沒有歷史資料,在存放資料量這一點上,都是一樣的。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-7qohvtwm-1607193049317)(E:\學習\寫作\別人的文章\最近可抄\Java-Summarize-master\image/Zookeeper-2.png)]

假設這些伺服器從id1-5,依序啟動:

因為一共5臺伺服器,只有超過半數以上,即最少啟動3臺伺服器,叢集才能正常工作。

(1)伺服器1啟動,發起一次選舉。
伺服器1投自己一票。此時伺服器1票數一票,不夠半數以上(3票),選舉無法完成;

伺服器1狀態保持為LOOKING;

(2)伺服器2啟動,再發起一次選舉。

伺服器1和2分別投自己一票,此時伺服器1發現伺服器2的id比自己大,更改選票投給伺服器2;

此時伺服器1票數0票,伺服器2票數2票,不夠半數以上(3票),選舉無法完成;

伺服器1,2狀態保持LOOKING;

(3)伺服器3啟動,發起一次選舉。

與上面過程一樣,伺服器1和2先投自己一票,然後因為伺服器3id最大,兩者更改選票投給為伺服器3;

此次投票結果:伺服器1為0票,伺服器2為0票,伺服器3為3票。此時伺服器3的票數已經超過半數(3票),伺服器3當選Leader。

伺服器1,2更改狀態為FOLLOWING,伺服器3更改狀態為LEADING;

(4)伺服器4啟動,發起一次選舉。

此時伺服器1,2,3已經不是LOOKING狀態,不會更改選票資訊。交換選票資訊結果:伺服器3為3票,伺服器4為1票。

此時伺服器4服從多數,更改選票資訊為伺服器3;

伺服器4並更改狀態為FOLLOWING;

(5)伺服器5啟動,同4一樣投票給3,此時伺服器3一共5票,伺服器5為0票;

伺服器5並更改狀態為FOLLOWING;

(6)選舉結果

最終Leader是伺服器3,狀態為LEADING;

其餘伺服器是Follower,狀態為FOLLOWING。

Zookeeper與Eureka的區別

結構上

1、Zookeeper是主從架構
2、Eureka是點對點,節點都是平級的

高可用與強一致性

1、Zookeeper當master掛了,會在30-120s進行leader選舉,這點類似於redis的哨兵機制,在選舉期間Zookeeper是不可用的,這麼長時間不能進行服務註冊,是無法忍受的,別說30s,5s都不能忍受。這時Zookeeper叢集會癱瘓,這也是Zookeeper的CP,保持節點的一致性,犧牲了A/高可用。而Eureka不會,,這就是AP,犧牲了C/一致性。

2、即使Eureka有部分掛掉,還有其他節點可以使用的,他們保持平級的關係,只不過資訊有可能不一致。

當壞掉的服務恢復的時候,會自動加入到節點上,也是高可用的一種。然後退出自我保護機制,這也是應對網路異常的一種機制

Eureka的自我保護機制

在預設配置中,Eureka Server在預設90s沒有得到客戶端的心跳,則登出該例項,但是往往因為微服務跨程式呼叫,網路通訊往往會面臨著各種問題,比如微服務狀態正常,但是因為網路分割槽故障時,Eureka Server登出服務例項則會讓大部分微服務不可用,這很危險,因為服務明明沒有問題。

為了解決這個問題,Eureka 有自我保護機制,通過在Eureka Server配置如下引數,可啟動保護機制

eureka.server.enable-self-preservation=true

如果在15分鐘內超過85%的節點都沒有正常的心跳,那麼Eureka就認為客戶端與註冊中心出現了網路故障,此時會出現以下幾種情況:

  1. Eureka不再從註冊列表中移除因為長時間沒收到心跳而應該過期的服務
  2. Eureka仍然能夠接受新服務的註冊和查詢請求,但是不會被同步到其它節點上(即保證當前節點依然可用)
  3. 當網路穩定時,當前例項新的註冊資訊會被同步到其它節點中

它的原理是,當Eureka Server節點在短時間內丟失過多的客戶端時(可能傳送了網路故障),那麼這個節點將進入自我保護模式,不再登出任何微服務,當網路故障恢復後,該節點會自動退出自我保護模式

自我保護模式的架構哲學是寧可放過一個,決不可錯殺一千

相關文章