Zookeeper原理詳解

雜文論 發佈 2022-10-21T22:46:02.238161+00:00

Zookeeper是什麼Zookeeper 分布式服務框架是Apache Hadoop 的一個子項目,它主要是用來解決分布式應用中經常遇到的一些數據管理問題,如:統一命名服務、狀態同步服務、集群管理、分布式應用配置項的管理等Zookeeper 作為一個分布式的服務框架,主要用來解

ZooKeeper是什麼

Zookeeper 分布式服務框架是Apache hadoop 的一個子項目,它主要是用來解決分布式應用中經常遇到的一些數據管理問題,如:統一命名服務、狀態同步服務、集群管理、分布式應用配置項的管理等

Zookeeper 作為一個分布式的服務框架,主要用來解決分布式集群中應用系統的一致性問題,它能提供基於類似於文件系統的目錄節點樹方式的數據存儲, Zookeeper 作用主要是用來維護和監控存儲的數據的狀態變化,通過監控這些數據狀態的變化,從而達到基於數據的集群管理

簡單的說,zookeeper=文件系統+通知機制。

Zookeeer工作原理

ZooKeeper是一個分布式的,開放源碼的分布式應用程式協調服務,它包含一個簡單的原語集,分布式應用程式可以基於它實現同步服務,配置維護和 命名服務等。Zookeeper是hadoop的一個子項目,其發展歷程無需贅述。在分布式應用中,由於工程師不能很好地使用鎖機制,以及基於消息的協調 機制不適合在某些應用中使用,因此需要有一種可靠的、可擴展的、分布式的、可配置的協調機制來統一系統的狀態

Zookeeper中的元素介紹

1、znode節點

有四種類型的znode:

1、PERSISTENT-持久化目錄節點

客戶端與zookeeper斷開連接後,該節點依舊存在

2、 PERSISTENT_SEQUENTIAL-持久化順序編號目錄節點

客戶端與zookeeper斷開連接後,該節點依舊存在,只是Zookeeper給該節點名稱進行順序編號

3、EPHEMERAL-臨時目錄節點

客戶端與zookeeper斷開連接後,該節點被刪除

4、EPHEMERAL_SEQUENTIAL-臨時順序編號目錄節點

客戶端與zookeeper斷開連接後,該節點被刪除,只是Zookeeper給該節點名稱進行順序編號

2、NameService 命名服務

這個似乎最簡單,在zookeeper的文件系統里創建一個目錄,即有唯一的path。在我們使用tborg無法確定上遊程序的部署機器時即可與下遊程序約定好path,通過path即能互相探索發現

1

這個主要是作為分布式命名服務,通過調用zk的create node api,能夠很容易創建一個全局唯一的path,這個path就可以作為一個名稱。

3、configuration 配置管理

現在把這些配置全部放到zookeeper上去,保存在 Zookeeper 的某個目錄節點中,然後所有相關應用程式對這個目錄節點進行監聽,一旦配置信息發生變化,每個應用程式就會收到 Zookeeper 的通知,然後從 Zookeeper 獲取新的配置信息應用到系統中就好。

4、GroupMembers 集群管理

所謂集群管理無在乎兩點:是否有機器退出和加入、選舉master。

對於第一點,所有機器約定在父目錄GroupMembers下創建臨時目錄節點,然後監聽父目錄節點的子節點變化消息。一旦有機器掛掉,該機器與 zookeeper的連接斷開,其所創建的臨時目錄節點被刪除,所有其他機器都收到通知:某個兄弟目錄被刪除,於是,所有人都知道了。新機器加入也是類似,所有機器收到通知:新兄弟目錄加入,highcount又有了。

對於第二點,所有機器創建臨時順序編號目錄節點,通過master選舉算法選舉出來。

1、Zookeeper的角色

1、Leader

  它是Zookeeper集群工作的核心,也是事務性請求(寫操作)的唯一調度和處理者,它保證集群事務處理的順序性,同時負責進行投票的發起和決議,以及更新系統狀態。

2、Follower

  它負責處理客戶端的非事務(讀操作)請求,如果接收到客戶端發來的事務性請求,則會轉發給Leader,讓Leader進行處理,同時還負責在Leader選舉過程中參與投票。

3、Observer

  它負責觀察Zookeeper集群的最新狀態的變化,並且將這些狀態進行同步。對於非事務性請求可以進行獨立處理;對於事務性請求,則會轉發給Leader伺服器進行處理。它不會參與任何形式的投票,只提供非事務性的服務,通常用於在不影響集群事務處理能力的前提下,提升集群的非事務處理能力(提高集群讀的能力,也降低了集群選主的複雜程度)。


4、客戶端(client),請求發起方

Zab協議是為分布式協調服務Zookeeper專門設計的一種 支持崩潰恢復 的 原子廣播協議 ,是Zookeeper保證數據一致性的核心算法。Zab借鑑了Paxos算法,但又不像Paxos那樣,是一種通用的分布式一致性算法。它是特別為Zookeeper設計的支持崩潰恢復的原子廣播協議。

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

Zab協議內容

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

協議過程

當整個集群啟動過程中,或者當 Leader 伺服器出現網絡中弄斷、崩潰退出或重啟等異常時,Zab協議就會 進入崩潰恢復模式,選舉產生新的Leader。

當選舉產生了新的 Leader,同時集群中有過半的機器與該 Leader 伺服器完成了狀態同步(即數據同步)之後,Zab協議就會退出崩潰恢復模式,進入消息廣播模式。

這時,如果有一台遵守Zab協議的伺服器加入集群,因為此時集群中已經存在一個Leader伺服器在廣播消息,那麼該新加入的伺服器自動進入恢復模式:找到Leader伺服器,並且完成數據同步。同步完成後,作為新的Follower一起參與到消息廣播流程中。

為了保證事務的順序一致性,zookeeper採用了遞增的事務id號(zxid)來標識事務。所有的提議(proposal)都在被提出的時候加上了zxid。實現中zxid是一個64位的數字,它高32位是epoch用來標識 leader關係是否改變,每次一個leader被選出來,它都會有一個新的epoch,標識當前屬於那個leader的統治時期。低32位用於遞增計數。

Zab協議的核心:定義了事務請求的處理方式

1)所有的事務請求必須由一個全局唯一的伺服器來協調處理,這樣的伺服器被叫做 Leader伺服器。其他剩餘的伺服器則是 Follower伺服器。

2)Leader伺服器 負責將一個客戶端事務請求,轉換成一個 事務Proposal,並將該 Proposal 分發給集群中所有的 Follower 伺服器,也就是向所有 Follower 節點發送數據廣播請求(或數據複製)

3)分發之後Leader伺服器需要等待所有Follower伺服器的反饋(Ack請求),在Zab協議中,只要超過半數的Follower伺服器進行了正確的反饋後(也就是收到半數以上的Follower的Ack請求),那麼 Leader 就會再次向所有的 Follower伺服器發送 Commit 消息,要求其將上一個 事務proposal 進行提交。

Leader選舉

1. Leader選舉概述

Leader選舉是保證分布式數據一致性的關鍵所在。當Zookeeper集群中的一台伺服器出現以下兩種情況之一時,需要進入Leader選舉。

(1) 伺服器初始化啟動。

(2) 伺服器運行期間無法和Leader保持連接。

下面就兩種情況進行分析講解。

1.1. 伺服器啟動時期的Leader選舉

若進行Leader選舉,則至少需要兩台機器,這裡選取3台機器組成的伺服器集群為例。在集群初始化階段,當有一台伺服器Server1啟動時,其單獨無法進行和完成Leader選舉,當第二台伺服器Server2啟動時,此時兩台機器可以相互通信,每台機器都試圖找到Leader,於是進入Leader選舉過程。選舉過程如下

(1) 每個Server發出一個投票。由於是初始情況,Server1和Server2都會將自己作為Leader伺服器來進行投票,每次投票會包含所推舉的伺服器的myid和ZXID,使用(myid, ZXID)來表示,此時Server1的投票為(1, 0),Server2的投票為(2, 0),然後各自將這個投票發給集群中其他機器。

(2) 接受來自各個伺服器的投票。集群的每個伺服器收到投票後,首先判斷該投票的有效性,如檢查是否是本輪投票、是否來自LOOKING狀態的伺服器。

(3) 處理投票。針對每一個投票,伺服器都需要將別人的投票和自己的投票進行PK,PK規則如下

· 優先檢查ZXID。ZXID比較大的伺服器優先作為Leader。

· 如果ZXID相同,那麼就比較myid。myid較大的伺服器作為Leader伺服器。

對於Server1而言,它的投票是(1, 0),接收Server2的投票為(2, 0),首先會比較兩者的ZXID,均為0,再比較myid,此時Server2的myid最大,於是更新自己的投票為(2, 0),然後重新投票,對於Server2而言,其無須更新自己的投票,只是再次向集群中所有機器發出上一次投票信息即可。

(4) 統計投票。每次投票後,伺服器都會統計投票信息,判斷是否已經有過半機器接受到相同的投票信息,對於Server1、Server2而言,都統計出集群中已經有兩台機器接受了(2, 0)的投票信息,此時便認為已經選出了Leader。

(5) 改變伺服器狀態。一旦確定了Leader,每個伺服器就會更新自己的狀態,如果是Follower,那麼就變更為FOLLOWING,如果是Leader,就變更為LEADING。

1.2. 伺服器運行時期的Leader選舉

在Zookeeper運行期間,Leader與非Leader伺服器各司其職,即便當有非Leader伺服器宕機或新加入,此時也不會影響Leader,但是一旦Leader伺服器掛了,那麼整個集群將暫停對外服務,進入新一輪Leader選舉,其過程和啟動時期的Leader選舉過程基本一致。假設正在運行的有Server1、Server2、Server3三台伺服器,當前Leader是Server2,若某一時刻Leader掛了,此時便開始Leader選舉。選舉過程如下

(1) 變更狀態。Leader掛後,餘下的非Observer伺服器都會講自己的伺服器狀態變更為LOOKING,然後開始進入Leader選舉過程。

(2) 每個Server會發出一個投票。在運行期間,每個伺服器上的ZXID可能不同,此時假定Server1的ZXID為123,Server3的ZXID為122;在第一輪投票中,Server1和Server3都會投自己,產生投票(1, 123),(3, 122),然後各自將投票發送給集群中所有機器。

(3) 接收來自各個伺服器的投票。與啟動時過程相同。

(4) 處理投票。與啟動時過程相同,此時,Server1將會成為Leader。

(5) 統計投票。與啟動時過程相同。

(6) 改變伺服器的狀態。與啟動時過程相同。


zookeeper中的watcher

Watcher是Zookeeper用來實現distribute lock, distribute configure, distribute queue等應用的主要手段。要監控data_tree上的任何節點的變化(節點本身的增加,刪除,數據修改,以及孩子的變化)都可以在獲取該數據時註冊一個Watcher,這有很像Listener模式。一旦該節點數據變化,Follower會發送一個notification response,client收到notification響應,則會查找對應的Watcher並回調他們

Client可以在某個ZNode上設置一個Watcher,來Watch該ZNode上的變化。如果該ZNode上有相應的變化,就會觸發這個Watcher,把相應的事件通知給設置Watcher的Client。需要注意的是,ZooKeeper中的Watcher是一次性的,即觸發一次就會被取消,如果想繼續Watch的話,需要客戶端重新設置Watcher

ZooKeeper Watcher 特性總結

註冊只能確保一次消費

無論是服務端還是客戶端,一旦一個 Watcher 被觸發,ZooKeeper 都會將其從相應的存儲中移除。因此,開發人員在 Watcher 的使用上要記住的一點是需要反覆註冊。這樣的設計有效地減輕了服務端的壓力。如果註冊一個 Watcher 之後一直有效,那麼針對那些更新非常頻繁的節點,服務端會不斷地向客戶端發送事件通知,這無論對於網絡還是服務端性能的影響都非常大。

客戶端串行執行

客戶端 Watcher 回調的過程是一個串行同步的過程,這為我們保證了順序,同時,需要開發人員注意的一點是,千萬不要因為一個 Watcher 的處理邏輯影響了整個客戶端的 Watcher 回調。

輕量級設計

WatchedEvent 是 ZooKeeper 整個 Watcher 通知機制的最小通知單元,這個數據結構中只包含三部分的內容:通知狀態、事件類型和節點路徑。也就是說,Watcher 通知非常簡單,只會告訴客戶端發生了事件,而不會說明事件的具體內容。例如針對 NodeDataChanged 事件,ZooKeeper 的 Watcher 只會通知客戶指定數據節點的數據內容發生了變更,而對於原始數據以及變更後的新數據都無法從這個事件中直接獲取到,而是需要客戶端主動重新去獲取數據,這也是 ZooKeeper 的 Watcher 機制的一個非常重要的特性。

ZooKeeper典型使用場景一覽

ZooKeeper是一個高可用的分布式數據管理與系統協調框架。基於對Paxos算法的實現,使該框架保證了分布式環境中數據的強一致性,也正是基 於這樣的特性,使得zookeeper能夠應用於很多場景。

1

數據發布與訂閱

發布與訂閱即所謂的配置管理,顧名思義就是將數據發布到zk節點上,供訂閱者動態獲取數據,實現配置信息的集中式管理和動態更新。例如全局的配置信息,地址列表等就非常適合使用。

索引信息和集群中機器節點狀態存放在zk的一些指定節點,供各個客戶端訂閱使用。

系統日誌(經過處理後的)存儲,這些日誌通常2-3天後被清除。

應用中用到的一些配置信息集中管理,在應用啟動的時候主動來獲取一次,並且在節點上註冊一個Watcher,以後每次配置有更新,實時通知到應用,獲取最新配置信息。

業務邏輯中需要用到的一些全局變量,比如一些消息中間件的消息隊列通常有個offset,這個offset存放在zk上,這樣集群中每個發送者都能知道當前的發送進度。

系統中有些信息需要動態獲取,並且還會存在人工手動去修改這個信息。以前通常是暴露出接口,例如JMX接口,有了zk後,只要將這些信息存放到zk節點上即可。

分布通知/協調

ZooKeeper 中特有watcher註冊與異步通知機制,能夠很好的實現分布式環境下不同系統之間的通知與協調,實現對數據變更的實時處理。使用方法通常是不同系統都對 ZK上同一個znode進行註冊,監聽znode的變化(包括znode本身內容及子節點的),其中一個系統update了znode,那麼另一個系統能 夠收到通知,並作出相應處理。

另一種心跳檢測機制:檢測系統和被檢測系統之間並不直接關聯起來,而是通過zk上某個節點關聯,大大減少系統耦合。

另一種系統調度模式:某系統有控制台和推送系統兩部分組成,控制台的職責是控制推送系統進行相應的推送工作。管理人員在控制台作的一些操作,實際上是修改 了ZK上某些節點的狀態,而zk就把這些變化通知給他們註冊Watcher的客戶端,即推送系統,於是,作出相應的推送任務。

另一種工作匯報模式:一些類似於任務分發系統,子任務啟動後,到zk來註冊一個臨時節點,並且定時將自己的進度進行匯報(將進度寫回這個臨時節點),這樣任務管理者就能夠實時知道任務進度。

總之,使用zookeeper來進行分布式通知和協調能夠大大降低系統之間的耦合。

分布式鎖

分布式鎖,這個主要得益於ZooKeeper為我們保證了數據的強一致性,即用戶只要完全相信每時每刻,zk集群中任意節點(一個zk server)上的相同znode的數據是一定是相同的。鎖服務可以分為兩類,一個是保持獨占,另一個是控制時序。

保持獨占,就是所有試圖來獲取這個鎖的客戶端,最終只有一個可以成功獲得這把鎖。通常的做法是把zk上的一個znode看作是一把鎖,通過create znode的方式來實現。所有客戶端都去創建 /distribute_lock 節點,最終成功創建的那個客戶端也即擁有了這把鎖。

控制時序,就是所有視圖來獲取這個鎖的客戶端,最終都是會被安排執行,只是有個全局時序了。做法和上面基本類似,只是這裡 /distribute_lock 已經預先存在,客戶端在它下面創建臨時有序節點(這個可以通過節點的屬性控制:CreateMode.EPHEMERAL_SEQUENTIAL來指定)。Zk的父節點(/distribute_lock)維持一份sequence,保證子節點創建的時序性,從而也形成了每個客戶端的全局時序。

集群管理

集群機器監控:這通常用於那種對集群中機器狀態,機器在線率有較高要求的場景,能夠快速對集群中機器變化作出響應。這樣的場景中,往往有一個監控系統,實時檢測集群機器是否存活。過去的做法通常是:監控系統通過某種手段(比如ping)定時檢測每個機器,或者每個機器自己定時向監控系統匯報「我還活著」。 這種做法可行,但是存在兩個比較明顯的問題:1. 集群中機器有變動的時候,牽連修改的東西比較多。2. 有一定的延時。

利用ZooKeeper有兩個特性,就可以實時另一種集群機器存活性監控系統:a. 客戶端在節點 x 上註冊一個Watcher,那麼如果 x 的子節點變化了,會通知該客戶端。b. 創建EPHEMERAL類型的節點,一旦客戶端和伺服器的會話結束或過期,那麼該節點就會消失。

Master選舉則是zookeeper中最為經典的使用場景了。

在分布式環境中,相同的業務應用分布在不同的機器上,有些業務邏輯(例如一些耗時的計算,網絡I/O處理),往往只需要讓整個集群中的某一台機器進行執行, 其餘機器可以共享這個結果,這樣可以大大減少重複勞動,提高性能,於是這個master選舉便是這種場景下的碰到的主要問題。

利用ZooKeeper的強一致性,能夠保證在分布式高並發情況下節點創建的全局唯一性,即:同時有多個客戶端請求創建 /currentMaster 節點,最終一定只有一個客戶端請求能夠創建成功。

關鍵字: