1. 程式人生 > >ZooKeeper基礎概念介紹 侵立刪

ZooKeeper基礎概念介紹 侵立刪

轉自:http://developer.51cto.com/art/201809/583184.htm

 

我本人曾經使用過 ZooKeeper 作為 Dubbo 的註冊中心,另外在搭建 Solr 叢集的時候,我使用到了 ZooKeeper 作為 Solr 叢集的管理工具。

前幾天,總結專案經驗的時候,我突然問自己 ZooKeeper 到底是個什麼東西?

想了半天,腦海中只是簡單的能浮現出幾句話:

  • Zookeeper 可以被用作註冊中心。
  • Zookeeper 是 Hadoop 生態系統的一員。
  • 構建 Zookeeper 叢集的時候,使用的伺服器最好是奇數臺。

可見,我對於 Zookeeper 的理解僅僅是停留在了表面。所以,通過本文,希望帶大家稍微詳細的瞭解一下 ZooKeeper 。

如果沒有學過 ZooKeeper,那麼本文將會是你進入 ZooKeeper 大門的墊腳磚;如果你已經接觸過 ZooKeeper ,那麼本文將帶你回顧一下 ZooKeeper 的一些基礎概念。

最後,本文只涉及 ZooKeeper 的一些概念,並不涉及 ZooKeeper 的使用以及 ZooKeeper 叢集的搭建。

網上有介紹 ZooKeeper 的使用以及搭建 ZooKeeper 叢集的文章,大家有需要可以自行查閱。

什麼是 ZooKeeper

ZooKeeper 的由來

下面這段內容摘自《從 Paxos 到 ZooKeeper 》第四章第一節的某段內容,推薦大家閱讀一下:

Zookeeper 最早起源於雅虎研究院的一個研究小組。在當時,研究人員發現,在雅虎內部很多大型系統基本都需要依賴一個類似的系統來進行分散式協調,但是這些系統往往都存在分散式單點問題。

所以,雅虎的開發人員就試圖開發一個通用的無單點問題的分散式協調框架,以便讓開發人員將精力集中在處理業務邏輯上。

關於“ZooKeeper”這個專案的名字,其實也有一段趣聞。在立項初期,考慮到之前內部很多專案都是使用動物的名字來命名的(例如著名的Pig專案),雅虎的工程師希望給這個專案也取一個動物的名字。

時任研究院的首席科學家 Raghu Ramakrishnan 開玩笑地說:“在這樣下去,我們這兒就變成動物園了!”

此話一出,大家紛紛表示就叫動物園管理員吧,因為各個以動物命名的分散式元件放在一起,雅虎的整個分散式系統看上去就像一個大型的動物園了。

而 Zookeeper 正好要用來進行分散式環境的協調,於是,Zookeeper 的名字也就由此誕生了。

ZooKeeper 概覽

ZooKeeper 是一個開源的分散式協調服務,ZooKeeper 框架最初是在“Yahoo!"上構建的,用於以簡單而穩健的方式訪問他們的應用程式。

後來,Apache ZooKeeper 成為 Hadoop,HBase 和其他分散式框架使用的有組織服務的標準。

例如,Apache HBase 使用 ZooKeeper 跟蹤分散式資料的狀態。

ZooKeeper 的設計目標是將那些複雜且容易出錯的分散式一致性服務封裝起來,構成一個高效可靠的原語集,並以一系列簡單易用的介面提供給使用者使用。

原語: 作業系統或計算機網路用語範疇。它是由若干條指令組成的,用於完成一定功能的一個過程。具有不可分割性,即原語的執行必須是連續的,在執行過程中不允許被中斷。

ZooKeeper 是一個典型的分散式資料一致性解決方案,分散式應用程式可以基於 ZooKeeper 實現諸如資料釋出/訂閱、負載均衡、命名服務、分散式協調/通知、叢集管理、Master 選舉、分散式鎖和分散式佇列等功能。

ZooKeeper 一個最常用的使用場景就是用於擔任服務生產者和服務消費者的註冊中心。

服務生產者將自己提供的服務註冊到 ZooKeeper 中心,服務的消費者在進行服務呼叫的時候先到 ZooKeeper 中查詢服務,獲取到服務生產者的詳細資訊之後,再去呼叫服務生產者的內容與資料。

如下圖所示,在 Dubbo 架構中 ZooKeeper 就擔任了註冊中心這一角色。

Dubbo 架構圖

結合個人使用講一下 ZooKeeper

在我自己做過的專案中,主要使用到了 ZooKeeper 作為 Dubbo 的註冊中心(Dubbo 官方推薦使用 ZooKeeper 註冊中心)。

另外在搭建 Solr 叢集的時候,我使用  ZooKeeper 作為 Solr 叢集的管理工具。

這時,ZooKeeper 主要提供下面幾個功能:

  • 叢集管理:容錯、負載均衡。
  • 配置檔案的集中管理。
  • 叢集的入口。

我個人覺得在使用 ZooKeeper 的時候,最好是使用叢集版的 ZooKeeper 而不是單機版的。

官網給出的架構圖就描述的是一個叢集版的 ZooKeeper 。通常 3 臺伺服器就可以構成一個  ZooKeeper 叢集了。

為什麼最好使用奇數臺伺服器構成 ZooKeeper 叢集?

我們知道在 ZooKeeper 中 Leader 選舉演算法採用了 Zab 協議。Zab 核心思想是當多數 Server 寫成功,則任務資料寫成功:

  • 如果有 3 個 Server,則最多允許 1 個 Server 掛掉。
  • 如果有 4 個 Server,則同樣最多允許 1 個 Server 掛掉。

既然 3 個或者 4 個 Server,同樣最多允許 1 個 Server 掛掉,那麼它們的可靠性是一樣的。

所以選擇奇數個 ZooKeeper Server 即可,這裡選擇 3 個 Server。

關於 ZooKeeper  的一些重要概念

重要概念總結

關於 ZooKeeper  的一些重要概念:

  • ZooKeeper 本身就是一個分散式程式(只要半數以上節點存活,ZooKeeper 就能正常服務)。
  • 為了保證高可用,最好是以叢集形態來部署 ZooKeeper,這樣只要叢集中大部分機器是可用的(能夠容忍一定的機器故障),那麼 ZooKeeper 本身仍然是可用的。
  • ZooKeeper 將資料儲存在記憶體中,這也就保證了 高吞吐量和低延遲(但是記憶體限制了能夠儲存的容量不太大,此限制也是保持 Znode 中儲存的資料量較小的進一步原因)。
  • ZooKeeper 是高效能的。在“讀”多於“寫”的應用程式中尤其地高效能,因為“寫”會導致所有的伺服器間同步狀態。(“讀”多於“寫”是協調服務的典型場景。)
  • ZooKeeper 有臨時節點的概念。當建立臨時節點的客戶端會話一直保持活動,瞬時節點就一直存在。

而當會話終結時,瞬時節點被刪除。持久節點是指一旦這個 ZNode 被建立了,除非主動進行 ZNode 的移除操作,否則這個 ZNode 將一直儲存在 Zookeeper 上。

  • ZooKeeper 底層其實只提供了兩個功能:①管理(儲存、讀取)使用者程式提交的資料;②為使用者程式提交資料節點監聽服務。

下面關於會話(Session)、 Znode、版本、Watcher、ACL 概念的總結都在《從 Paxos 到 ZooKeeper 》第四章第一節以及第七章第八節有提到,感興趣的可以看看!

會話(Session)

Session 指的是 ZooKeeper  伺服器與客戶端會話。在 ZooKeeper 中,一個客戶端連線是指客戶端和伺服器之間的一個 TCP 長連線。

客戶端啟動的時候,首先會與伺服器建立一個 TCP 連線,從第一次連線建立開始,客戶端會話的生命週期也開始了。

通過這個連線,客戶端能夠通過心跳檢測與伺服器保持有效的會話,也能夠向 Zookeeper 伺服器傳送請求並接受響應,同時還能夠通過該連線接收來自伺服器的 Watch 事件通知。

Session 的 sessionTimeout 值用來設定一個客戶端會話的超時時間。

當由於伺服器壓力太大、網路故障或是客戶端主動斷開連線等各種原因導致客戶端連線斷開時,只要在 sessionTimeout 規定的時間內能夠重新連線上叢集中任意一臺伺服器,那麼之前建立的會話仍然有效。

在為客戶端建立會話之前,服務端首先會為每個客戶端都分配一個 sessionID。

由於 sessionID 是 Zookeeper 會話的一個重要標識,許多與會話相關的執行機制都是基於這個 sessionID 的。

因此,無論是哪臺伺服器為客戶端分配的 sessionID,都務必保證全域性唯一。

Znode

在談到分散式的時候,我們通常說的“節點"是指組成叢集的每一臺機器。

然而,在 ZooKeeper 中,“節點"分為兩類:

  • 第一類同樣是指構成叢集的機器,我們稱之為機器節點。
  • 第二類則是指資料模型中的資料單元,我們稱之為資料節點一ZNode。

ZooKeeper 將所有資料儲存在記憶體中,資料模型是一棵樹(Znode Tree),由斜槓(/)的進行分割的路徑,就是一個 Znode,例如/foo/path1。每個上都會儲存自己的資料內容,同時還會儲存一系列屬性資訊。

在 Zookeeper 中,Node 可以分為持久節點和臨時節點兩類。所謂持久節點是指一旦這個 ZNode 被建立了,除非主動進行 ZNode 的移除操作,否則這個 ZNode 將一直儲存在 ZooKeeper 上。

而臨時節點就不一樣了,它的生命週期和客戶端會話繫結,一旦客戶端會話失效,那麼這個客戶端建立的所有臨時節點都會被移除。

另外,ZooKeeper 還允許使用者為每個節點新增一個特殊的屬性:SEQUENTIAL。

一旦節點被標記上這個屬性,那麼在這個節點被建立的時候,ZooKeeper 會自動在其節點名後面追加上一個整型數字,這個整型數字是一個由父節點維護的自增數字。

版本

在前面我們已經提到,Zookeeper 的每個 ZNode 上都會儲存資料,對應於每個 ZNode,Zookeeper 都會為其維護一個叫作 Stat 的資料結構。

Stat 中記錄了這個 ZNode 的三個資料版本,分別是:

  • version(當前 ZNode 的版本)
  • cversion(當前 ZNode 子節點的版本)
  • aversion(當前 ZNode 的 ACL 版本)

Watcher

Watcher(事件監聽器),是 ZooKeeper 中的一個很重要的特性。

ZooKeeper 允許使用者在指定節點上註冊一些 Watcher,並且在一些特定事件觸發的時候,ZooKeeper 服務端會將事件通知到感興趣的客戶端上去,該機制是 ZooKeeper 實現分散式協調服務的重要特性。

ACL

ZooKeeper 採用 ACL(AccessControlLists)策略來進行許可權控制,類似於  UNIX 檔案系統的許可權控制。

ZooKeeper 定義了 5 種許可權,如下圖:

其中尤其需要注意的是,CREATE 和 DELETE 這兩種許可權都是針對子節點的許可權控制。

ZooKeeper 特點

ZooKeeper 有哪些特點呢?具體如下:

  • 順序一致性:從同一客戶端發起的事務請求,最終將會嚴格地按照順序被應用到 ZooKeeper 中去。
  • 原子性:所有事務請求的處理結果在整個叢集中所有機器上的應用情況是一致的,也就是說,要麼整個叢集中所有的機器都成功應用了某一個事務,要麼都沒有應用。
  • 單一系統映像:無論客戶端連到哪一個 ZooKeeper 伺服器上,其看到的服務端資料模型都是一致的。
  • 可靠性:一旦一次更改請求被應用,更改的結果就會被持久化,直到被下一次更改覆蓋。

ZooKeeper 設計目標

簡單的資料模型

ZooKeeper 允許分散式程序通過共享的層次結構名稱空間進行相互協調,這與標準檔案系統類似。

名稱空間由 ZooKeeper 中的資料暫存器組成,稱為 Znode,這些類似於檔案和目錄。

與為儲存設計的典型檔案系統不同,ZooKeeper 資料儲存在記憶體中,這意味著 ZooKeeper 可以實現高吞吐量和低延遲。

可構建叢集

為了保證高可用,最好是以叢集形態來部署 ZooKeeper,這樣只要叢集中大部分機器是可用的(能夠容忍一定的機器故障),那麼 ZooKeeper 本身仍然是可用的。

客戶端在使用 ZooKeeper 時,需要知道叢集機器列表,通過與叢集中的某一臺機器建立 TCP 連線來使用服務。

客戶端使用這個 TCP 連結來發送請求、獲取結果、獲取監聽事件以及傳送心跳包。如果這個連線異常斷開了,客戶端可以連線到另外的機器上。

ZooKeeper 官方提供的架構圖:

上圖中每一個 Server 代表一個安裝 ZooKeeper 服務的伺服器。組成 ZooKeeper 服務的伺服器都會在記憶體中維護當前的伺服器狀態,並且每臺伺服器之間都互相保持著通訊。

叢集間通過 Zab 協議(Zookeeper Atomic Broadcast)來保持資料的一致性。

順序訪問

對於來自客戶端的每個更新請求,ZooKeeper 都會分配一個全域性唯一的遞增編號。

這個編號反應了所有事務操作的先後順序,應用程式可以使用 ZooKeeper 這個特性來實現更高層次的同步原語。這個編號也叫做時間戳—zxid(ZooKeeper Transaction Id)。

高效能

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

ZooKeeper 叢集角色介紹

最典型叢集模式:Master/Slave 模式(主備模式)。在這種模式中,通常 Master 伺服器作為主伺服器提供寫服務,其他的 Slave 伺服器從伺服器通過非同步複製的方式獲取 Master 伺服器最新的資料提供讀服務。

但是,在 ZooKeeper 中沒有選擇傳統的 Master/Slave 概念,而是引入了Leader、Follower 和 Observer 三種角色。

如下圖所示:

ZooKeeper 叢集中的所有機器通過一個 Leader 選舉過程來選定一臺稱為 “Leader” 的機器。

Leader 既可以為客戶端提供寫服務又能提供讀服務。除了 Leader 外,Follower 和  Observer 都只能提供讀服務。

Follower 和 Observer 唯一的區別在於 Observer 機器不參與 Leader 的選舉過程,也不參與寫操作的“過半寫成功”策略(zookeeper叢集的寫操作,由leader節點負責,它會把通知所有節進行寫入操作,只有收到半數以上節點的成功反饋,才算成功。如果是部署2個節點的話,那就必須都成功。),因此 Observer 機器可以在不影響寫效能的情況下提升叢集的讀效能。

ZooKeeper & ZAB 協議 & Paxos 演算法

ZAB 協議 & Paxos 演算法

Paxos 演算法可以說是  ZooKeeper 的靈魂了。但是,ZooKeeper 並沒有完全採用 Paxos 演算法 ,而是使用 ZAB 協議作為其保證資料一致性的核心演算法。

另外,在 ZooKeeper 的官方文件中也指出,ZAB 協議並不像 Paxos 演算法那樣,是一種通用的分散式一致性演算法,它是一種特別為 ZooKeeper 設計的崩潰可恢復的原子訊息廣播演算法。

ZAB 協議介紹

ZAB(ZooKeeper Atomic Broadcast 原子廣播)協議是為分散式協調服務 ZooKeeper 專門設計的一種支援崩潰恢復的原子廣播協議。

在 ZooKeeper 中,主要依賴 ZAB 協議來實現分散式資料一致性,基於該協議,ZooKeeper 實現了一種主備模式的系統架構來保持叢集中各個副本之間的資料一致性。

ZAB 協議兩種基本的模式

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

當整個服務框架在啟動過程中,或是當 Leader 伺服器出現網路中斷、崩潰退出與重啟等異常情況時,ZAB 協議就會進入恢復模式並選舉產生新的 Leader 伺服器。

當選舉產生了新的 Leader 伺服器,同時叢集中已經有過半的機器與該 Leader 伺服器完成了狀態同步之後,ZAB 協議就會退出恢復模式。

其中,所謂的狀態同步是指資料同步,用來保證叢集中存在過半的機器能夠和 Leader 伺服器的資料狀態保持一致。

當叢集中已經有過半的 Follower 伺服器完成了和 Leader 伺服器的狀態同步,那麼整個服務框架就可以進人訊息廣播模式了。

當一臺同樣遵守 ZAB 協議的伺服器啟動後加入到叢集中時,如果此時叢集中已經存在一個 Leader 伺服器在負責進行訊息廣播。

那麼新加入的伺服器就會自覺地進人資料恢復模式:找到 Leader 所在的伺服器,並與其進行資料同步,然後一起參與到訊息廣播流程中去。

正如上文介紹中所說的,ZooKeeper 設計成只允許唯一的一個 Leader 伺服器來進行事務請求的處理。

Leader 伺服器在接收到客戶端的事務請求後,會生成對應的事務提案併發起一輪廣播協議。

而如果叢集中的其他機器接收到客戶端的事務請求,那麼這些非 Leader 伺服器會首先將這個事務請求轉發給 Leader 伺服器。

關於 ZAB 協議 & Paxos 演算法需要講和理解的東西太多了,推薦閱讀下面兩篇文章:

  • 圖解 Paxos 一致性協議:

http://blog.xiaohansong.com/2016/09/30/Paxos/

  • Zookeeper ZAB 協議分析:

http://blog.xiaohansong.com/2016/08/25/zab/

關於如何使用 ZooKeeper 實現分散式鎖,可以檢視下面這篇文章:

  • Zookeeper ZAB 協議分析:

https://blog.csdn.net/qiangcuo6087/article/details/79067136

總結

通過閱讀本文,想必大家已從以下這七點了解了 ZooKeeper:

  • ZooKeeper 的由來
  • ZooKeeper 到底是什麼
  • ZooKeeper 的一些重要概念(會話(Session)、Znode、版本、Watcher、ACL)
  • ZooKeeper 的特點
  • ZooKeeper 的設計目標
  • ZooKeeper 叢集角色介紹(Leader、Follower 和 Observer 三種角色)
  • ZooKeeper & ZAB 協議 & Paxos 演算法

參考文章:

  • 《從Paxos到Zookeeper 》
  • https://cwiki.apache.org/confluence/display/ZOOKEEPER/ProjectDescription
  • https://cwiki.apache.org/confluence/display/ZOOKEEPER/Index
  • https://www.cnblogs.com/raphael5200/p/5285583.html
  • https://zhuanlan.zhihu.com/p/30024403

zookeeper部署奇數節點的原因(最少3個)

1.zookeeper叢集的寫操作,由leader節點負責,它會把通知所有節進行寫入操作,只有收到半數以上節點的成功反饋,才算成功。如果是部署2個節點的話,那就必須都成功。

2.zookeeper的選舉策略也是需要半數以上的節點同意才能當選leader,如果是偶數節點可能導致票數相同的情況

3.只有當半數以上的節點存活時 zookeeper叢集才能對外服務,維持正常狀態,如果是2個節點,只要其中一個掛掉,那麼剩下的1個並不滿足半數以上規則。
 

zookeeper的讀寫流程

基本架構

節點數要求是奇數。

常用的介面是 get/set/create/getChildren.

讀寫流程

寫流程

客戶端連線到叢集中某一個節點

客戶端傳送寫請求

服務端連線節點,把該寫請求轉發給leader

leader處理寫請求,一半以上的從節點也寫成功,返回給客戶端成功。

讀流程

客戶端連線到叢集中某一節點

讀請求,直接返回。

故障恢復 && leader選舉

當leader down掉時。

叢集暫停服務,進行leader選舉,採用fast paxos協議

首先所有server,提交自己作為leader,log的ID(epoch+1),id作用互動資料

通過比較接收的日誌事務Id和自身的事務ID。

等待一個週期,確定出最新的leader。

載入snapshot,執行log。

最終一致性

讀資料時,有可能會髒讀。比較推薦watch的方式,實現資料的及時生效。

各節點資料完全一致

各節點儲存了全量的資料。

儲存策略

持久化儲存是基於記憶體快照(snapshot)和事務日誌(txlog)來儲存。

snapshot和txlog的儲存目錄定義在zoo.cfg中,txlog儲存磁碟和snapshot儲存磁碟分開,避免io爭奪。

txlog的刷盤閾值是1000。txlog是生成snapshot之後生成。

snapshot的儲存數量和清理時間間隔配置在zoo.cfg中。

時間複雜度

zookeeper 使用concurrenthashmap進行儲存。鎖的粒度是segment,減少鎖競爭,segment裡對應一個hashtable 的若干桶.

所以時間複雜度都是 O(1)