1. 程式人生 > >8. 理解ZooKeeper的內部工作原理

8. 理解ZooKeeper的內部工作原理

zab 階段 身份驗證 過多 管理系統 多個 基礎 des jpg

到目前為止,我們已經討論了ZooKeeper服務的基礎知識,並詳細了解了數據模型及其屬性。 我們也熟悉了ZooKeeper 監視(watch)的概念,監視就是在ZooKeeper命名空間中的znode發生任何變化時完成的事件機制。 我們通過公開一組與znodes相關聯的ACL來讀取身份驗證和基本安全模型。

在本節中,我們將通過介紹ZooKeeper session的概念來討論和了解客戶端與ZooKeeper服務交互的生命周期。 我們還將詳細閱讀ZooKeeper如何在內部描述協議。 了解以及深入理解內部工作原理非常重要,這有助於使用ZooKeeper設計分布式應用程序,並了解與之相關的錯綜復雜的事情。

我們先來看看客戶端如何與ZooKeeper服務進行交互。 為了使分布式應用程序能夠使用ZooKeeper服務,他們必須通過客戶端類庫來使用API。 ZooKeeper客戶端庫對幾乎所有流行的編程語言都有語言綁定。 客戶端庫類庫負責應用程序與ZooKeeper服務的交互。

下圖顯示了應用程序與ZooKeeper服務的交互過程:
技術分享圖片

ZooKeeper服務可以以兩種模式運行:獨立(standalone)模式和仲裁(quorum)模式。 在獨立模式下,有一個ZooKeeper服務器。 另一方面,quorum模式意味著ZooKeeper以復制模式運行在一組機器上,也稱為ensemble。

Note
獨立模式僅用於評估和測試應用程序代碼,但不能在生產中使用,因為這是潛在的單點故障。 在仲裁模式下,ZooKeeper通過復制實現高可用性,只要ensemble中大部分機器啟動,都可以提供服務。

一 quorum模式

ZooKeeper quorum構成了復制節點的大部分,這些復制節點將ZooKeeper服務的最新狀態存儲在ensemble中的所有服務器中。 這基本上是必須啟動並運行的服務器節點的最小數量,並且可用於客戶端請求。 客戶端對ZooKeeper樹進行的任何更新都必須永久存儲在此quorum的節點中,以便事務成功完成。

例如,在一個五個節點ensemble中,任何兩臺機器都可能失敗,並且我們可以擁有三臺服務器的quorum,ZooKeeper服務仍然可以工作。 稍後,如果其他兩個失敗節點出現,則可以通過從現有quorum中獲取最新狀態來同步ZooKeeper服務狀態。

Tips

調整ZooKeeper服務中服務器節點的數量對於ZooKeeper正確運行非常重要。 由於所有的事務提交都依賴於多數共識的概念,所以建議ZooKeeper集合中應該有奇數個機器。

讓我們來看一個例子,看看為什麽這是有道理的。 假設我們有一個由五臺服務器組成的ZooKeeper ensemble。 如果任何兩臺服務器發生故障,ensemble仍然可以運行,因為可以在其余三個節點之外形成quorum。 因此,五節點ZooKeeper ensemble可以容忍多達兩個節點的故障。

現在,對於六節點ensemble,ZooKeeper服務可以容忍最多只有兩個節點的故障。 這是因為有三個節點失敗,無法形成quorum。 在那裏不能達到多數的共識。 同樣,ZooKeeper quorum必須保證任何成功承認客戶端的事務都應該是持久的,並在形成quorum的節點上可見。

如果ZooKeeper quorum不是由ensemble中的大多數節點組成的,ZooKeeper命名空間的狀態可能會有不一致,從而導致錯誤的結果。 除了節點故障之外,集合中節點之間的網絡分區可能會導致不一致的操作,因為quorum成員之間將無法傳遞更新。 這導致在分布式群集中出現的共同問題,稱為腦裂(split-brain)。

腦裂是ensemble中的兩個服務器子集獨立運作的場景。 這會導致整個ZooKeeper服務中的狀態不一致,並且不同的客戶端根據相同的請求獲得不同的結果,具體取決於它們所連接的服務器。 通過使用奇數個節點運行ZooKeeper集群,我們可以將此類錯誤的概率降至概率最小。

二 客戶端與ZooKeeper服務建立session會話

連接到ZooKeeper的客戶端可以配置一個構成ZooKeeper ensemble的服務器列表。 客戶端嘗試通過從列表中選擇一個隨機服務器來連接到列表中的服務器。 如果連接失敗,則嘗試連接到下一個服務器,依此類推。 此過程直到列表中的所有服務器都被嘗試或建立了成功的連接。

一旦客戶端和ZooKeeper服務器之間建立連接,就在客戶端和服務器之間建立一個會話(session),表示為分配給客戶端的64位數字。 會話的概念對於ZooKeeper的運行非常重要。 會話與客戶在ZooKeeper服務中執行的每個操作相關聯。

會話在ZooKeeper中扮演著非常重要的角色。 例如,ephemeral節點的整個概念是基於客戶端和ZooKeeper服務器之間會話的概念。 ephemeral znode在客戶端和ZooKeeper之間有會話的生命周期; 當這個會話結束時,這些節點將被ZooKeeper服務自動刪除。

會話有一個超時期限,在連接到ZooKeeper服務時由應用程序或客戶端指定。 客戶端發送一個請求的超時作為創建連接調用中的參數來創建一個以毫秒為單位指定的ZooKeeper。 如果連接保持空閑超過超時時間,則會話可能會過期。 會話到期由ZooKeeper集群本身管理,而不是由客戶端管理。 當前的實現要求超時至少是tickTime的兩倍,最多為tickTime的20倍。

指定正確的會話超時取決於各種因素,如網絡擁塞,應用程序邏輯的復雜性,甚至ZooKeeper ensemble的大小。 例如,在一個非常繁忙和擁擠的網絡中,如果延遲很高,那麽會話超時會非常低,會導致會話過期。 同樣,如果你的ensemble很大,建議有一個更大的超時時間。 此外,如果應用程序看到頻繁的連接丟失,增加會話超時可能會有用。 然而,另一個警告是它不應該對應用程序的核心邏輯產生不經意的影響。

客戶端通過向ZooKeeper服務發送ping請求(心跳)來保持活動。 這些心跳是由客戶端類庫自動發送的,因此,應用程序員不必擔心會話保持活躍狀態。 客戶端和ZooKeeper服務器之間的會話使用TCP連接進行維護。 兩個連續的心跳之間的間隔應該保持低,這樣客戶端和ZooKeeper服務器之間的連接失敗可以很早被檢測到,並且可以進行重新連接嘗試。 重新連接到另一個ZooKeeper服務器通常由客戶端類庫以透明方式完成。 當重新連接到同一個ensemble的不同服務器時,客戶端創建的現有會話和關聯的ephemeral znode仍然有效。 對於在客戶端和服務器之間維護的單個會話,ZooKeeper保證通常按照FIFO順序的排序。

如前一節所述,應用程序使用客戶端類庫與ZooKeeper服務建立會話。 表示連接對象的句柄由ZooKeeper客戶端API返回給應用程序。 這個ZooKeeper連接對象在其創建和結束之間的時間內通過不同的狀態轉換。 連接對象會持續到客戶端程序的連接正常關閉或會話由於超時而終止。

一旦連接對象被創建,它就從CONNECTING狀態開始,客戶端庫嘗試連接到ZooKeeper ensemble中的一個服務器。 當連接到ZooKeeper服務時,對象轉換到CONNECTED狀態。 由於事件(如會話到期和身份驗證失敗),或者應用程序正常關閉使用庫調用的連接,對象的狀態將轉到CLOSED狀態。

下圖顯示了ZooKeeper客戶端會話的狀態轉換:
技術分享圖片

三 ZooKeeper事務的實現

在前面的章節中,我們看到了ZooKeeper如何在服務器上運行,以及客戶端如何連接到這些服務器,建立會話並在ZooKeeper服務中執行操作。 在服務器ensemble中,服務器被選為領導者,剩下的所有服務器都成為追隨者。 領導處理所有更改ZooKeeper服務的請求。 追隨者收到領導者的更新,並通過多數共識機制,在整個ensemble中保持一致的狀態。 ZooKeeper服務負責替換失敗的領導者,並將跟隨者與領導者同步,整個過程對於客戶端應用程序是完全透明的。

該服務依靠復制機制來確保所有更新在構成ensemble的所有服務器中都是永久的。 每個服務器都維護著一個核心數據庫,它代表了ZooKeeper命名空間的整個狀態。 為了確保更新是持久的,並在服務器崩潰時可以恢復,更新記錄到本地磁盤。 此外,寫入操作在序列化到磁盤之後才會應用到內存數據庫。

ZooKeeper使用一個稱為 ZooKeeper Atomic Broadcast (ZAB)的特定原子消息廣播協議。 該協議確保ensemble中的本地副本永不分離。 此外,ZAB協議是原子的,所以協議保證更新要不全部成功,或者全部失敗。

復制的數據庫、ZAB協議與領導者選舉機制一起構成了ZooKeeper服務實現的核心。 ZooKeeper服務名稱空間中的更新或寫入以及讀取操作由這些核心組件處理,如下圖所示,也可以在http://zookeeper.apache.org/doc/r3.4.6/zookeeperOver.html#fg_zkComponents中找到:

技術分享圖片

在ZooKeeper實現中,讀取請求(如existsgetDatagetChildren)由客戶端連接的ZooKeeper服務器本地處理。 這使得ZooKeeper中的讀操作非常快。 寫入或更新請求,例如createdeletesetData被轉發給ensemble中的領導者。 領導者執行客戶請求作為一個事務。 這個事務類似於數據庫管理系統中事務的概念。

ZooKeeper事務也包含成功執行請求所需的所有步驟作為單個工作單元,並且以原子方式應用更新。 此外,事務滿足隔離性,這意味著任何事務都不會受到任何其他事務的幹擾。 ZooKeeper服務中的事務是冪等的。 事務通過一個事務標識符(zxid)來標識,它是一個64位整數,分為兩部分:紀元和計數器,每個部分都是32位。

事務處理包括ZooKeeper的兩個步驟:領導選舉和原子消息廣播協議。 這類似於兩階段提交協議,也包括領導者選舉和原子消息廣播協議。

四 第一階段——領導者選舉

ensemble中的服務器經過一個選舉主服務器的過程,稱為領導者。 ensemble中的其他服務器被稱為追隨者。

參與領導選舉算法的每個服務器都有一個名為“LOOKING”的狀態。如果一個領導者已經存在於整個系統中,那麽對等服務器將通知新的參與者服務器關於現有的領導者。在了解領導者後,新的服務器與領導者保持同步狀態。

一個領導者不存在於ensemble中時,ZooKeeper在服務器ensemble中運行領導者選舉算法。 在這種情況下,首先,所有服務器都處於LOOKING狀態。 該算法指示服務器交換消息以選舉領導者。 當參與者服務器匯聚在一個特定服務器的共同選擇上時,該算法就會停止,這個服務器就成為領導者。 贏得這次選舉的服務器進入LEADING狀態,而ensemble中的其他服務器進入FOLLOWING狀態。

參與者服務器與他們的對等服務器交換的消息,包含服務器的標識符(sid)和它所執行的最近事務ID(zxid)。每個參與服務器在接收到對等服務器的消息後,將自己的sid和zxid與它所接收的消息進行比較。如果接收到的zxid大於服務器所持有的zxid,則服務器接受接收到的zxid,否則,它將自己的zxid設置並將其自己的zxid設置為ensemble中的對等服務器。

在這個算法的最後,擁有最近事務ID(zxid)的服務器贏得了領導選舉算法。在算法完成之後,追隨者服務器將其狀態與所選出的領導者同步。

領導者選舉的下一步是領導者激活。 新當選的領導者提出了NEW_LEADER建議,並且只有在NEW_LEADER提案被ensemble中的大多數服務器(quorum)確認之後,領導才會被激活。在新領導人的NEW_LEADER提議被提交之前,新領導人不會接受新提議。

五 第二階段——原子消息廣播協議

ZooKeeper中的所有寫入請求都被轉發給領導者。 領導者向ensemble的追隨者廣播其更新。 只有在大多數追隨者承認他們堅持這個變化之後,領導者才會進行更新。 ZooKeeper使用ZAB協議來實現共識,這被設計為原子的。 因此,更新成功或失敗。 在領導失敗時,ensemble中的其他服務器進入領導者選舉算法,以選出其中的新領導者。

Tips
ZAB: High-performance broadcast for primary-backup systems by Junqueira, F.P; Reed, > B.C; Serafini. M

(LADIS 2008, in: Proceedings of the 2nd Workshop on Large-Scale Distributed Systems > and Middleware)

可以通過以下鏈接訪問IEEE Xplore上的ZAB文章:http://bit.ly/1v3N1NN

ZAB保證了在遞送事務和提交事務時的嚴格順序。通過原子消息處理事務可以如下圖所示:
技術分享圖片

兩階段提交保證了事務的順序。 在協議中,一旦quorum確認一個事務,領導者就提交,而追隨者將其確認記錄在磁盤上。

Tips
除了領導者和追隨者之外,ZooKeeper ensemble中的服務器還有第三個特征,稱為觀察者。 觀察者和追隨者在概念上是相似的,因為他們都承諾領導者的建議。 然而,與追隨者不同,觀察者不參與兩階段提交過程的投票過程。 觀察者有助於ZooKeeper服務中讀取請求的可伸縮性,並有助於在跨越多個數據中心的ZooKeeper集成中傳播更新。

六 本地存儲和快照

ZooKeeper服務器使用本地存儲來保存事務。 事務記錄到事務日誌中,類似於在數據庫系統中使用的順序附加日誌文件的方法。 ZooKeeper服務器使用預先分配的文件將事務刷新到磁盤介質上。 在ZooKeeper事務處理的兩階段協議中,只有在強制將事務寫入事務日誌後,服務器才會確認提議。 由於ZooKeeper事務日誌的寫入速度很快,所以在一個與服務器的引導設備分開的磁盤中配置事務日誌是非常重要的。

ZooKeeper服務中的服務器還保存ZooKeeper樹或命名空間的時間點副本或快照到本地文件系統。 服務器不需要與ensemble的其他成員協調來保存這些快照。 此外,快照處理與ZooKeeper服務器的正常運行進行異步操作。

ZooKeeper快照文件和事務日誌能夠在發生災難性故障或用戶錯誤時恢復數據。 數據目錄由ZooKeeper配置文件中的dataDir參數指定,數據日誌目錄由dataLogDir參數指定。

8. 理解ZooKeeper的內部工作原理