很遺憾,沒(méi)有一篇文章能講清楚ZooKeeper
原創(chuàng)【51CTO.com原創(chuàng)稿件】互聯(lián)網(wǎng)時(shí)代是信息爆發(fā)的時(shí)代,信息的高并發(fā)催生了分布式系統(tǒng)的廣泛應(yīng)用。
圖片來(lái)自 Pexels
作為分布式系統(tǒng)解決方案的 ZooKeeper,被廣泛應(yīng)用于多個(gè)分布式場(chǎng)景。例如:數(shù)據(jù)發(fā)布/訂閱,負(fù)載均衡,命名服務(wù),集群管理等等。
因此,ZooKeeper 在分布式系統(tǒng)中扮演著重要的角色,今天通過(guò)一個(gè)簡(jiǎn)單的例子來(lái)看看它的實(shí)現(xiàn)原理。
從一個(gè)簡(jiǎn)單的例子開(kāi)始
在分布式系統(tǒng)中經(jīng)常會(huì)遇到這種情況,多個(gè)應(yīng)用讀取同一個(gè)配置。例如:A,B 兩個(gè)應(yīng)用都會(huì)讀取配置 C 中的內(nèi)容,一旦 C 中的內(nèi)容出現(xiàn)變化,會(huì)通知 A 和 B。
一般的做法是在 A,B 中按照時(shí)鐘頻率詢(xún)問(wèn) C 的變化,或者使用觀察者模式來(lái)監(jiān)聽(tīng) C 的變化,發(fā)現(xiàn)變化以后再更新 A 和 B。那么 ZooKeeper 如何協(xié)調(diào)這種場(chǎng)景?
ZooKeeper 會(huì)建立一個(gè) ZooKeeper 服務(wù)器,暫且稱(chēng)為 ZServer,用它來(lái)存放 C 的值。為 A,B 兩個(gè)應(yīng)用分別生成兩個(gè)客戶(hù)端,稱(chēng)作 ClientA 和 ClientB。
這兩個(gè)客戶(hù)端連接到 ZooKeeper 的服務(wù)器,并獲取其中存放的 C。保存 C 值的地方在 ZooKeeper 服務(wù)端(Server)中稱(chēng)為 ZNode。
ClientA 和 ClientB 通過(guò) ZooKeeper Server 獲取 C 的值
ZNode
通過(guò)上面的例子,客戶(hù)端 ClientA 和 ClientB 需要讀取 C 的內(nèi)容。這個(gè) C 就作為樹(shù)的葉子節(jié)點(diǎn)存放在 ZooKeeper 的 ZNode 中。
通常來(lái)說(shuō),為了提高效率 ZNode 是被存放在內(nèi)存中的。ZNode 的數(shù)據(jù)模型是一棵樹(shù)(ZNode Tree)。
好像我們從上圖中看到的一樣,樹(shù)中的每個(gè)節(jié)點(diǎn)都可以存放數(shù)據(jù),并且每個(gè)節(jié)點(diǎn)下面都可以存放葉子節(jié)點(diǎn)。
ZooKeeper 客戶(hù)端通過(guò) “/” 作為訪問(wèn)路徑,訪問(wèn)數(shù)據(jù)。例如可以通過(guò)路徑 “/RootNode/C” 來(lái)訪問(wèn) C 變量。
為了方便客戶(hù)端調(diào)用,ZooKeeper 會(huì)暴露一些命令:
訪問(wèn) Znode 命令
作為存儲(chǔ)媒介來(lái)說(shuō),ZNode分為持久節(jié)點(diǎn)和臨時(shí)節(jié)點(diǎn):
- 持久節(jié)點(diǎn)(PERSISTENT),該數(shù)據(jù)節(jié)點(diǎn)被創(chuàng)建后,就一直存在于 ZooKeeper 服務(wù)器上,除非刪除操作(delete)清除該節(jié)點(diǎn)。
- 臨時(shí)節(jié)點(diǎn)(EPHEMERAL),該數(shù)據(jù)節(jié)點(diǎn)的生命周期會(huì)和客戶(hù)端(Client)會(huì)話(huà)(Session)綁定在一起。如果客戶(hù)端(Client)會(huì)話(huà)丟失了,那么節(jié)點(diǎn)就自動(dòng)清除掉。
如果把臨時(shí)節(jié)點(diǎn)看成資源的話(huà),當(dāng)客戶(hù)端和服務(wù)端產(chǎn)生會(huì)話(huà)并生成臨時(shí)節(jié)點(diǎn),一旦客戶(hù)端與服務(wù)器中斷聯(lián)系,節(jié)點(diǎn)資源會(huì)被從 ZNode 中刪除。
順序節(jié)點(diǎn)(SEQUENTIAL),ZNode 節(jié)點(diǎn)被分配唯一個(gè)單調(diào)遞增的整數(shù)。例如多個(gè)客戶(hù)端在服務(wù)器 /tasks 上申請(qǐng)節(jié)點(diǎn)時(shí),根據(jù)客戶(hù)端申請(qǐng)的先后順序,將數(shù)字追加到 /tasks/task 后面。
如果有三個(gè)客戶(hù)端申請(qǐng)節(jié)點(diǎn)資源,那么在 /tasks 下面建立三個(gè)順序節(jié)點(diǎn),分別是 /tasks/task1,/tasks/task2,/tasks/task3。
順序節(jié)點(diǎn),在處理分布式事務(wù)的時(shí)候非常有幫助,當(dāng)多個(gè)客戶(hù)端(Client)協(xié)作工作的時(shí)候,會(huì)按照一定的順序執(zhí)行。
如果將上面的兩類(lèi)節(jié)點(diǎn)和順序節(jié)點(diǎn)進(jìn)行組合的話(huà),就有四種節(jié)點(diǎn)類(lèi)型,分別是持久節(jié)點(diǎn),持久順序節(jié)點(diǎn),臨時(shí)節(jié)點(diǎn),臨時(shí)順序節(jié)點(diǎn)。
Watcher
上面說(shuō)了 ZooKeeper 用來(lái)存放數(shù)據(jù)的 ZNode,并且把 C 的值存儲(chǔ)在里面。如果 C 被更新了,兩個(gè)客戶(hù)端(ClientA、ClientB)如何獲得通知呢?
ZooKeeper 客戶(hù)端(Client)會(huì)在指定的節(jié)點(diǎn)(/RootNote/C)上注冊(cè)一個(gè) Watcher,ZNode 上的 C 被更新的時(shí)候,服務(wù)端就會(huì)通知 ClientA 和 ClientB。
通過(guò)三步來(lái)實(shí)現(xiàn):
- 客戶(hù)端注冊(cè) Watcher
- 服務(wù)端處理 Watcher
- 客戶(hù)端回調(diào) Watcher
Watcher 注冊(cè),處理,回調(diào)
①客戶(hù)端注冊(cè) Watcher
ZooKeeper 客戶(hù)端創(chuàng)建 Watcher 的實(shí)例對(duì)象:
同時(shí)這個(gè) Watcher 會(huì)保存在客戶(hù)端本地,一直作為和服務(wù)端會(huì)話(huà)的 Watcher。
客戶(hù)端可以通過(guò) getData,getChildren 和 exist 方法來(lái)向服務(wù)端注冊(cè) Watcher。
客戶(hù)端注冊(cè) Watcher 簡(jiǎn)圖
同時(shí)需要注意的是在客戶(hù)端發(fā)送 Watcher 到服務(wù)端注冊(cè)的時(shí)候,會(huì)將這個(gè)要發(fā)送的 Watcher 在本地的 ZKWatchManager 中保存。
這樣做的好處,就是當(dāng)獲得服務(wù)端的注冊(cè)成功的信息以后,就不用將 Watcher 的具體內(nèi)容回傳給客戶(hù)端了。
客戶(hù)端只用在接到服務(wù)端響應(yīng)以后,從本地的 ZKWatchManager 中獲取 Watch 的信息進(jìn)行處理即可。
②服務(wù)端處理 Watcher
服務(wù)端收到客戶(hù)端的請(qǐng)求以后,交給 FinalRequestProcessor 處理,這個(gè)進(jìn)程會(huì)去 ZNode 中獲取對(duì)應(yīng)的數(shù)據(jù),同時(shí)會(huì)把 Watch 加入到 WatchManager 中。
這樣下次這節(jié)點(diǎn)上的數(shù)據(jù)被更改了以后,就會(huì)通知注冊(cè) Watch 的客戶(hù)端了。

服務(wù)端處理 Watch 過(guò)程
③客戶(hù)端回調(diào) Watcher
客戶(hù)端在響應(yīng)客戶(hù)端 Watcher 注冊(cè)以后,會(huì)發(fā)送 WathcerEvent 事件。作為客戶(hù)端有對(duì)應(yīng)的回調(diào)函數(shù)接受這個(gè)消息。
這里會(huì)通過(guò) readResponse 方法統(tǒng)一處理:

在 SendTread 接受到服務(wù)端的通知以后,會(huì)將事件通過(guò) EventThread.queueEvent 發(fā)送給 EventThread。
正如前面提到的,在客戶(hù)端注冊(cè)時(shí),已經(jīng)將 Watcher 的具體內(nèi)容保存在 ZKWatchManager 一樣了。
所以,EventTread 通過(guò) EventType 就可以知道哪個(gè) Watcher 被響應(yīng)了(數(shù)據(jù)變化了)。
然后從 ZKWatchManager 取出具體 Watch 放到 waitingEvent 隊(duì)列等待處理。
最后,由 EventThread 中的 processEvent 方法依次處理數(shù)據(jù)更新的響應(yīng)。
版本(Version)
介紹完了 Watcher 機(jī)制,回頭再來(lái)談?wù)?ZNode 的版本(Version)。如果有一個(gè)客戶(hù)端(ClientD),它嘗試修改 C 的值,此時(shí)其他兩個(gè)客戶(hù)端會(huì)收到通知,并且進(jìn)行后續(xù)的業(yè)務(wù)處理了。
那么在分布式系統(tǒng)中,會(huì)出現(xiàn)這么一種情況:在 ClientD 對(duì) C 進(jìn)行寫(xiě)入操作的時(shí)候,又有一個(gè) ClientE 也對(duì) C 進(jìn)行寫(xiě)入操作。這兩個(gè) Client 會(huì)去競(jìng)爭(zhēng) C 資源,通常這種情況需要對(duì) C 進(jìn)行加鎖操作。
兩個(gè) Client 競(jìng)爭(zhēng)一個(gè)資源
因此引入 ZNode 版本(Version)概念。版本是用來(lái)保證分布式數(shù)據(jù)原子性操作的。
ZNode 的版本(Version)信息保存在 ZNode 的 Stat 對(duì)象中。有如下三種:
本例只關(guān)注“數(shù)據(jù)節(jié)點(diǎn)內(nèi)容的版本號(hào)”,也就是 Version。
如果說(shuō) ClientD 和 ClientE 對(duì) C 進(jìn)行寫(xiě)入操作視作是一個(gè)事務(wù)的話(huà)。在執(zhí)行寫(xiě)入操作之前,兩個(gè)事務(wù)分別會(huì)獲取節(jié)點(diǎn)上的值,即節(jié)點(diǎn)保存的數(shù)據(jù)和節(jié)點(diǎn)的版本號(hào)(Version)。
以樂(lè)觀鎖為例,對(duì)數(shù)據(jù)的寫(xiě)入會(huì)分成以下三個(gè)階段:數(shù)據(jù)讀取,寫(xiě)入校驗(yàn)和數(shù)據(jù)寫(xiě)入。例如 C 上的數(shù)據(jù)是 1, Version 是 0。
此時(shí) ClientD 和 ClientE 都獲取了這些信息。假設(shè) ClientD 先做寫(xiě)入操作,在做寫(xiě)入校驗(yàn)的時(shí)候,發(fā)現(xiàn)之前獲得的 Version 和節(jié)點(diǎn)上的 Version 是相同的,都是 1,因此直接執(zhí)行數(shù)據(jù)寫(xiě)入。
寫(xiě)入以后,Version 由原來(lái)的 1 變成了 2。當(dāng) ClientE 做寫(xiě)入校驗(yàn)時(shí),發(fā)現(xiàn)自己持有的 Version=1 和節(jié)點(diǎn)當(dāng)前的 Version=2,不一樣。于是,寫(xiě)入失敗,重新獲取 Version 和節(jié)點(diǎn)數(shù)據(jù),再次嘗試寫(xiě)入。
除了上述方案以外,還可以利用 ZNode 的有序性。在 C 下面建立多個(gè)有序的子節(jié)點(diǎn)。每當(dāng)一個(gè) Client 準(zhǔn)備寫(xiě)入數(shù)據(jù)的時(shí)候,創(chuàng)建一個(gè)臨時(shí)有序的節(jié)點(diǎn)。
節(jié)點(diǎn)的順序根據(jù) FIFO 算法,保證先申請(qǐng)寫(xiě)入的 Client 排在其前面。每個(gè)節(jié)點(diǎn)都有一個(gè)序號(hào),后面申請(qǐng)的節(jié)點(diǎn)按照序號(hào)依次遞增。
ClientD,ClientE 分別建立子 ZNode
每個(gè) Client 在執(zhí)行修改 C 操作的時(shí)候,都要檢查有沒(méi)有比自己序號(hào)小的節(jié)點(diǎn),如果存在那么就進(jìn)入等待。
直到比自己序號(hào)小的節(jié)點(diǎn)進(jìn)行完畢以后,才輪到自己執(zhí)行修改操作。從而保證了事物處理的順序性。
會(huì)話(huà)(Session)
說(shuō)完版本(Version)的概念,例子從原來(lái)的 ClientAB 已經(jīng)擴(kuò)充到了 ClientDE。這些客戶(hù)端都會(huì)和 ZooKeeper 的服務(wù)端進(jìn)行通信,或讀取數(shù)據(jù)或修改數(shù)據(jù)。
我們將客戶(hù)端與服務(wù)端完成的這種連接稱(chēng)為會(huì)話(huà)。ZooKeeper 的會(huì)話(huà)有 Connecting,Connected,Reconnecting,Reconnected 和 Close 這幾種狀態(tài)。
并且在服務(wù)端由專(zhuān)門(mén)的進(jìn)程來(lái)管理他們,客戶(hù)端初始化的時(shí)候就會(huì)根據(jù)配置自動(dòng)連接服務(wù)器,從而建立會(huì)話(huà),客戶(hù)端連接服務(wù)器時(shí)會(huì)話(huà)處于 Connecting 狀態(tài)。
一旦連接完成,就會(huì)進(jìn)入 Connected 狀態(tài)。如果出現(xiàn)延遲或者短暫失聯(lián),客戶(hù)端會(huì)自動(dòng)重連,Reconnecting 和 Reconnected 狀態(tài)也就應(yīng)運(yùn)而生。
如果長(zhǎng)時(shí)間超時(shí),或者客戶(hù)端斷開(kāi)服務(wù)器,ZooKeeper 會(huì)清理掉會(huì)話(huà),以及該會(huì)話(huà)創(chuàng)建的臨時(shí)數(shù)據(jù)節(jié)點(diǎn),并且關(guān)閉和客戶(hù)端的連接。
- Session 作為會(huì)話(huà)實(shí)體,用來(lái)代表客戶(hù)端會(huì)話(huà),其包括 4 個(gè)屬性:
- SessionID,用來(lái)全局唯一識(shí)別會(huì)話(huà)。
- TimeOut,會(huì)話(huà)超時(shí)事件??蛻?hù)端在創(chuàng)造 Session 實(shí)例的時(shí)候,會(huì)設(shè)置一個(gè)會(huì)話(huà)超時(shí)的時(shí)間。
- TickTime,下次會(huì)話(huà)超時(shí)時(shí)間點(diǎn)。后面“分桶策略”會(huì)用到。
isClosing,當(dāng)服務(wù)端如果檢測(cè)到會(huì)話(huà)超時(shí)失效了,會(huì)通過(guò)設(shè)置這個(gè)屬性將會(huì)話(huà)關(guān)閉。
既然,會(huì)話(huà)是客戶(hù)端與服務(wù)器之間的連接。在服務(wù)器端由 SessionTracker 管理會(huì)話(huà)。
SessionTracker 有一個(gè)工作就是,將超時(shí)的會(huì)話(huà)清除掉。于是“分桶策略”就登場(chǎng)了。
由于每個(gè)會(huì)話(huà)在生成的時(shí)候都會(huì)定義超時(shí)時(shí)間,通過(guò)當(dāng)前時(shí)間+超時(shí)時(shí)間可以算出會(huì)話(huà)的過(guò)期時(shí)間。
由于 SessionTracker 不是實(shí)時(shí)監(jiān)聽(tīng)會(huì)話(huà)超時(shí),它是按照一定時(shí)間周期來(lái)監(jiān)聽(tīng)的。
也就是說(shuō),如果沒(méi)有到達(dá) SessionTracker 的檢查時(shí)間周期,即使有會(huì)話(huà)過(guò)期,SessionTracker 也不會(huì)去清除。由此,就引入會(huì)話(huà)超時(shí)計(jì)算公式,也就是 TickTime 的計(jì)算公式。
TickTime=((當(dāng)前時(shí)間+會(huì)話(huà)過(guò)期時(shí)間)/檢查時(shí)間間隔+1)*檢查時(shí)間間隔。
將這個(gè)值計(jì)算出來(lái)以后,SessionTracker 會(huì)把對(duì)應(yīng)的會(huì)話(huà)按照這個(gè)時(shí)間放在對(duì)應(yīng)的時(shí)間軸上面。SessionTracker 在對(duì)應(yīng)的 TickTime 檢查會(huì)話(huà)是否過(guò)期。
計(jì)算會(huì)話(huà)下次的過(guò)期時(shí)間
每當(dāng)客戶(hù)端連接上服務(wù)器都會(huì)做激活操作,同時(shí)每隔一段時(shí)間客戶(hù)端會(huì)向服務(wù)器發(fā)送心跳檢測(cè)。
服務(wù)器收到激活或者心跳檢測(cè)以后,會(huì)重新計(jì)算會(huì)話(huà)過(guò)期時(shí)間,根據(jù)“分桶策略”進(jìn)行重新調(diào)整。把會(huì)話(huà)從“老的區(qū)塊“放到”新的區(qū)塊“中去。
重新計(jì)算過(guò)期時(shí)間并且調(diào)整“分桶策略”
對(duì)于超時(shí)的會(huì)話(huà),SessionTracker 也會(huì)做如下清理工作:
- 標(biāo)記會(huì)話(huà)狀態(tài)為“已關(guān)閉”,也就是設(shè)置 isClosing 為 True。
- 發(fā)起“會(huì)話(huà)關(guān)閉”的請(qǐng)求,讓關(guān)閉操作在整個(gè)集群生效。
- 收集需要清理的臨時(shí)節(jié)點(diǎn)。
- 添加“節(jié)點(diǎn)刪除”的事務(wù)變更。
- 刪除臨時(shí)節(jié)點(diǎn)
- 移除會(huì)話(huà)
- 關(guān)閉客戶(hù)端與服務(wù)端的連接
會(huì)話(huà)關(guān)閉以后客戶(hù)端就無(wú)法從服務(wù)端獲取/寫(xiě)入數(shù)據(jù)了。
服務(wù)群組(Leader,F(xiàn)ollower,Observer)
前面提到了客戶(hù)端如何通過(guò)會(huì)話(huà)與服務(wù)端保持聯(lián)系,以及服務(wù)端是如何管理客戶(hù)端會(huì)話(huà)(Session)的。
我們繼續(xù)思考一下,這么多的服務(wù)端都依賴(lài)一個(gè) ZooKeeper 服務(wù)器。一旦服務(wù)掛了,客戶(hù)端就無(wú)法工作了。
為了提高 ZooKeeper 服務(wù)的可靠性,引入服務(wù)器集群的概念。從原來(lái)的單個(gè)服務(wù)器,擴(kuò)充成多個(gè)服務(wù)器,即使某一臺(tái)服務(wù)器掛了,其他的服務(wù)器也可以頂上來(lái)。
ZooKeeper 的服務(wù)器集群
這樣看起來(lái)不錯(cuò)了,新的問(wèn)題是,存在多個(gè) ZooKeeper 服務(wù)器,那么客戶(hù)端的請(qǐng)求發(fā)給哪臺(tái)呢?服務(wù)器之間如何同步數(shù)據(jù)呢?如果一個(gè)服務(wù)掛掉了其他的服務(wù)器如何替代?這里介紹兩個(gè)概念 Leader 和 Follower。
Leader 服務(wù)器,是事務(wù)請(qǐng)求(寫(xiě)操作)的唯一調(diào)度者和處理者,保證集群事務(wù)處理的順序性。也是集群內(nèi)部服務(wù)器的調(diào)度者。
它是整個(gè)集群的老大,其他的服務(wù)器接到事務(wù)請(qǐng)求都會(huì)轉(zhuǎn)交給它,讓它協(xié)調(diào)處理。
Follower 服務(wù)器,處理非事務(wù)請(qǐng)求(讀操作),轉(zhuǎn)發(fā)事務(wù)請(qǐng)求給 Leader 服務(wù)器。參與選舉 Leader 的投票和事務(wù)請(qǐng)求 Proposal 的投票。
既然 Leader 是集群的老大,那么這個(gè)老大是如何產(chǎn)生的。ZooKeeper 有仲裁機(jī)制,通過(guò)服務(wù)器的選舉產(chǎn)生這個(gè) Leader,按照少數(shù)服從多數(shù)的原則。
因此,集群中服務(wù)器的個(gè)數(shù)一般都是奇數(shù),例如:1,3,5。當(dāng)然這里是建議。關(guān)于選舉和仲裁都有一定的算法,一起來(lái)看看吧。
當(dāng)眾多服務(wù)器啟動(dòng)的時(shí)候,互相都不知道誰(shuí)是 Leader,因此都會(huì)進(jìn)入 Looking 狀態(tài),也就是在網(wǎng)絡(luò)中尋找 Leader。
尋找的過(guò)程也是投票的過(guò)程,每個(gè)服務(wù)器會(huì)將服務(wù)器 ID 和事務(wù) ID 作為投票信息發(fā)送給網(wǎng)絡(luò)中其他的服務(wù)器。假設(shè)稱(chēng)它為投票信息 VOTE,它包括:(ServerID,ZXID)。
其中,ServerID 是服務(wù)器注冊(cè)的 ID,隨著服務(wù)器啟動(dòng)的順序自動(dòng)增加,后啟動(dòng)的服務(wù)器 ServerID 就大;ZXID 是服務(wù)器處理事物的 ID,隨著事物的增加自動(dòng)增加,同樣后提交的事務(wù) ZXID 也大一些。
其他的服務(wù)器收到 VOTE 信息以后會(huì)和自己的 VOTE 信息(ServerID,ZXID)進(jìn)行比較。
如果收到的 VOTE(ServerID,ZXID)中的 ZXID 比自己的 ZXID 要大,那么把自己的 VOTE 修改成收到的 VOTE。
如果 ZXID 一樣大,那么就比較 ServerID,將大的那個(gè) ServerID 作為自己 VOTE 的 ServerID,轉(zhuǎn)發(fā)給其他服務(wù)器。
再簡(jiǎn)單點(diǎn)說(shuō),如果事務(wù) ID(ZXID)比自己的事務(wù) ID(ZXID)要大,就把票投給這個(gè)服務(wù)器。如果事務(wù) ID 一樣,就把票投給 ServerID 大的服務(wù)器。
來(lái)個(gè)具體的例子,有三個(gè)服務(wù)器,他們的投票值分別是:
- S1 (1,6)
- S2 (2,5)
- S3 (3,5)
三個(gè)服務(wù)器分別把自己的 VOTE 發(fā)給其他兩臺(tái)服務(wù)器,S2 和 S3 收到 VOTE 以后發(fā)現(xiàn) ZXID 為 6 的來(lái)自 S1 的 VOTE 比自己持有的 ZXID 要大,因此把自己的 VOTE 修改為(1,6)投出去,因此 S1 稱(chēng)為 Leader。
Leader 選舉實(shí)例
同樣,如果 S1 作為 Leader,因?yàn)槟撤N原因掛掉或者長(zhǎng)時(shí)間沒(méi)有響應(yīng)請(qǐng)求,其他的服務(wù)器也會(huì)進(jìn)入 Looking 狀態(tài),開(kāi)啟投票仲裁模式尋找下一個(gè) Leader。
成為新 Leader 以后會(huì)通過(guò)廣播的方式將 ZNode 上的數(shù)據(jù)同步到其他的 Follower。
Leader 有了,整個(gè)服務(wù)器集群有了領(lǐng)袖,它可以處理客戶(hù)端的事物請(qǐng)求。客戶(hù)端的請(qǐng)求可以發(fā)給集群中任意一臺(tái)服務(wù)器,無(wú)論是哪個(gè)服務(wù)器都會(huì)將事物請(qǐng)求轉(zhuǎn)交給 Leader。
Leader 在將數(shù)據(jù)寫(xiě)入 ZNode 之前會(huì)向 ZooKeeper 的其他 Follower 進(jìn)行廣播。
這里廣播用到了 ZAB 協(xié)議(Atomic Broadcast Protocol)是 Paxos 協(xié)議的實(shí)踐。說(shuō)白了就是一個(gè)兩段提交。
PS:對(duì)分布式事務(wù)比較了解的同學(xué)應(yīng)該知道兩段提交和三段提交。
這里 ZooKeeper 通過(guò)以下方式實(shí)現(xiàn)兩段提交:
- Leader 向所有 Follower 發(fā)送一個(gè) PROPOSAL。
- 當(dāng) Follower 接收到 PROPOSAL 后,返回給 Leader 一個(gè) ACK 消息,表示我收到 PROPOSAL,并且準(zhǔn)備好了。
- Leader 仲裁數(shù)量(過(guò)半數(shù))的 Follower 發(fā)送的 ACK 后(包括 Leader 自己),會(huì)發(fā)送消息通知 Follower 進(jìn)行 COMMIT。
- 收到 COMMIT 以后,F(xiàn)ollower 就開(kāi)始干活,將數(shù)據(jù)寫(xiě)入到 ZNode 中。
ZAB 廣播 PROPOSAL
選舉了 Leader 領(lǐng)導(dǎo)集群,Leader 接受到 Client 的請(qǐng)求以后,也可以協(xié)調(diào) Follower 工作了。
那么如果 Client 很多的情況下,特別是這些客戶(hù)端都是做讀操作的時(shí)候,ZooKeeper 服務(wù)器如何處理如此多的請(qǐng)求呢?這里引入 Observer 的概念。
Observer 和 Follower 基本一致,對(duì)于非事務(wù)請(qǐng)求(讀操作),可以直接返回節(jié)點(diǎn)中的信息(數(shù)據(jù)從 Leader 中同步過(guò)來(lái)的)。
對(duì)于事務(wù)請(qǐng)求(寫(xiě)操作),會(huì)轉(zhuǎn)交給 Leader 做統(tǒng)一處理。Observer 的存在就是為了解決大量客戶(hù)端讀請(qǐng)求。
Observer 和 Follower 的區(qū)別是,Observer 不參與仲裁投票,選舉 Leader。
Observer 加入 Leader 和 Follower 大家庭
總結(jié)
全文用了一個(gè)簡(jiǎn)單的例子講 ZooKeeper 的主要特性和實(shí)現(xiàn)原理,最后做個(gè)總結(jié)。
ZooKeeper 被用來(lái)協(xié)調(diào)和管理分布式系統(tǒng),發(fā)揮著重要的作用。分布式系統(tǒng)由于其特性,應(yīng)用分布在不同的物理主機(jī)或者網(wǎng)絡(luò)中。
為了讓它們協(xié)同工作,ZooKeeper 中的 ZNode 成為統(tǒng)一協(xié)調(diào)的重要部分,客戶(hù)端通過(guò) Client 間接到服務(wù)端的 ZNode 上,監(jiān)聽(tīng) ZNode 數(shù)據(jù)的變化。
同時(shí) ZNode 支持的持久,臨時(shí)和順序性,以及版本(Version)控制,這些特性支持了分布式事務(wù)和鎖的功能。
如果說(shuō),每一個(gè) ZooKeeperClient 對(duì) Server 的寫(xiě)入操作都是一次事務(wù)的話(huà),ZooKeeper 服務(wù)端維護(hù)了大量的事務(wù),并且通過(guò)“分桶策略”來(lái)管理它們,保證了 Client 與 Server 端協(xié)調(diào)工作。
為了提高 Server 的可靠性,ZooKeeper 引入了 Server 集群的概念。通過(guò)仲裁機(jī)制選舉 Leader 來(lái)領(lǐng)導(dǎo)其他 Follower。
事物都由 Leader 來(lái)處理,通過(guò)兩段提交的方式對(duì)其他 Server 發(fā)起廣播。為了增強(qiáng)對(duì)非事務(wù)請(qǐng)求的處理效率,ZooKeeper 加入了 Observer 來(lái)幫忙。
ZooKeeper 包含的內(nèi)容遠(yuǎn)不止上面說(shuō)的這些,由于篇幅的原因無(wú)法一一道來(lái)。
為了方便大家理解,文中將一些原理做了簡(jiǎn)化處理,希望有機(jī)會(huì)和大家做深入的探討,咱們下次見(jiàn)。
作者:崔皓
簡(jiǎn)介:十六年開(kāi)發(fā)和架構(gòu)經(jīng)驗(yàn),曾擔(dān)任過(guò)惠普武漢交付中心技術(shù)專(zhuān)家,需求分析師,項(xiàng)目經(jīng)理,后在創(chuàng)業(yè)公司擔(dān)任技術(shù)/產(chǎn)品經(jīng)理。善于學(xué)習(xí),樂(lè)于分享。目前專(zhuān)注于技術(shù)架構(gòu)與研發(fā)管理。
【51CTO原創(chuàng)稿件,合作站點(diǎn)轉(zhuǎn)載請(qǐng)注明原文作者和出處為51CTO.com】