日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

很遗憾,没有一篇文章能讲清楚ZooKeeper

發布時間:2025/3/21 编程问答 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 很遗憾,没有一篇文章能讲清楚ZooKeeper 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

作為分布式系統解決方案的 ZooKeeper,被廣泛應用于多個分布式場景。例如:數據發布/訂閱,負載均衡,命名服務,集群管理等等。

因此,ZooKeeper 在分布式系統中扮演著重要的角色,今天通過一個簡單的例子來看看它的實現原理。

從一個簡單的例子開始

在分布式系統中經常會遇到這種情況,多個應用讀取同一個配置。例如:A,B 兩個應用都會讀取配置 C 中的內容,一旦 C 中的內容出現變化,會通知 A 和 B。

一般的做法是在 A,B 中按照時鐘頻率詢問 C 的變化,或者使用觀察者模式來監聽 C 的變化,發現變化以后再更新 A 和 B。那么 ZooKeeper 如何協調這種場景?

ZooKeeper 會建立一個 ZooKeeper 服務器,暫且稱為 ZServer,用它來存放 C 的值。為 A,B 兩個應用分別生成兩個客戶端,稱作 ClientA 和 ClientB。

這兩個客戶端連接到 ZooKeeper 的服務器,并獲取其中存放的 C。保存 C 值的地方在 ZooKeeper 服務端(Server)中稱為 ZNode。

ClientA 和 ClientB 通過 ZooKeeper Server 獲取 C 的值

ZNode

通過上面的例子,客戶端 ClientA 和 ClientB 需要讀取 C 的內容。這個 C 就作為樹的葉子節點存放在 ZooKeeper 的 ZNode 中。

通常來說,為了提高效率 ZNode 是被存放在內存中的。ZNode 的數據模型是一棵樹(ZNode Tree)。

好像我們從上圖中看到的一樣,樹中的每個節點都可以存放數據,并且每個節點下面都可以存放葉子節點。

ZooKeeper 客戶端通過?“/” 作為訪問路徑,訪問數據。例如可以通過路徑 “/RootNode/C” 來訪問 C 變量。

為了方便客戶端調用,ZooKeeper 會暴露一些命令:

訪問 Znode 命令

作為存儲媒介來說,ZNode分為持久節點和臨時節點:

  • 持久節點(PERSISTENT),該數據節點被創建后,就一直存在于?ZooKeeper?服務器上,除非刪除操作(delete)清除該節點。

  • 臨時節點(EPHEMERAL),該數據節點的生命周期會和客戶端(Client)會話(Session)綁定在一起。如果客戶端(Client)會話丟失了,那么節點就自動清除掉。

如果把臨時節點看成資源的話,當客戶端和服務端產生會話并生成臨時節點,一旦客戶端與服務器中斷聯系,節點資源會被從 ZNode 中刪除。

順序節點(SEQUENTIAL),ZNode 節點被分配唯一個單調遞增的整數。例如多個客戶端在服務器 /tasks 上申請節點時,根據客戶端申請的先后順序,將數字追加到 /tasks/task 后面。

如果有三個客戶端申請節點資源,那么在 /tasks 下面建立三個順序節點,分別是?/tasks/task1,/tasks/task2,/tasks/task3。

順序節點,在處理分布式事務的時候非常有幫助,當多個客戶端(Client)協作工作的時候,會按照一定的順序執行。

如果將上面的兩類節點和順序節點進行組合的話,就有四種節點類型,分別是持久節點,持久順序節點,臨時節點,臨時順序節點。

Watcher

上面說了 ZooKeeper 用來存放數據的 ZNode,并且把 C 的值存儲在里面。如果 C 被更新了,兩個客戶端(ClientA、ClientB)如何獲得通知呢?

ZooKeeper 客戶端(Client)會在指定的節點(/RootNote/C)上注冊一個 Watcher,ZNode 上的 C 被更新的時候,服務端就會通知 ClientA 和 ClientB。

通過三步來實現:

  • 客戶端注冊 Watcher

  • 服務端處理?Watcher

  • 客戶端回調 Watcher

Watcher?注冊,處理,回調

①客戶端注冊 Watcher

ZooKeeper 客戶端創建 Watcher 的實例對象:

同時這個 Watcher 會保存在客戶端本地,一直作為和服務端會話的 Watcher。

客戶端可以通過 getData,getChildren 和 exist 方法來向服務端注冊 Watcher。

客戶端注冊 Watcher 簡圖

同時需要注意的是在客戶端發送 Watcher 到服務端注冊的時候,會將這個要發送的 Watcher 在本地的 ZKWatchManager 中保存。

這樣做的好處,就是當獲得服務端的注冊成功的信息以后,就不用將 Watcher 的具體內容回傳給客戶端了。

客戶端只用在接到服務端響應以后,從本地的 ZKWatchManager 中獲取 Watch 的信息進行處理即可。

②服務端處理 Watcher

服務端收到客戶端的請求以后,交給 FinalRequestProcessor 處理,這個進程會去 ZNode 中獲取對應的數據,同時會把 Watch 加入到 WatchManager 中。

這樣下次這節點上的數據被更改了以后,就會通知注冊 Watch 的客戶端了。

服務端處理 Watch 過程

③客戶端回調 Watcher

客戶端在響應客戶端 Watcher 注冊以后,會發送 WathcerEvent 事件。作為客戶端有對應的回調函數接受這個消息。

這里會通過 readResponse 方法統一處理:

在 SendTread 接受到服務端的通知以后,會將事件通過 EventThread.queueEvent 發送給 EventThread。

正如前面提到的,在客戶端注冊時,已經將 Watcher 的具體內容保存在 ZKWatchManager 一樣了。

所以,EventTread 通過 EventType 就可以知道哪個 Watcher 被響應了(數據變化了)。

然后從 ZKWatchManager 取出具體 Watch 放到 waitingEvent 隊列等待處理。

最后,由 EventThread 中的 processEvent 方法依次處理數據更新的響應。

?

版本(Version)

介紹完了 Watcher 機制,回頭再來談談 ZNode 的版本(Version)。如果有一個客戶端(ClientD),它嘗試修改 C 的值,此時其他兩個客戶端會收到通知,并且進行后續的業務處理了。

那么在分布式系統中,會出現這么一種情況:在 ClientD 對 C 進行寫入操作的時候,又有一個 ClientE 也對 C 進行寫入操作。這兩個 Client 會去競爭 C 資源,通常這種情況需要對 C 進行加鎖操作。

兩個 Client 競爭一個資源

因此引入 ZNode 版本(Version)概念。版本是用來保證分布式數據原子性操作的。

ZNode 的版本(Version)信息保存在 ZNode 的 Stat 對象中。有如下三種:

本例只關注“數據節點內容的版本號”,也就是 Version。

如果說 ClientD 和 ClientE 對 C 進行寫入操作視作是一個事務的話。在執行寫入操作之前,兩個事務分別會獲取節點上的值,即節點保存的數據和節點的版本號(Version)。

以樂觀鎖為例,對數據的寫入會分成以下三個階段:數據讀取,寫入校驗和數據寫入。例如 C 上的數據是 1, Version 是 0。

此時 ClientD 和 ClientE 都獲取了這些信息。假設 ClientD 先做寫入操作,在做寫入校驗的時候,發現之前獲得的 Version 和節點上的 Version 是相同的,都是 1,因此直接執行數據寫入。

寫入以后,Version 由原來的 1 變成了 2。當 ClientE 做寫入校驗時,發現自己持有的 Version=1 和節點當前的 Version=2,不一樣。于是,寫入失敗,重新獲取 Version 和節點數據,再次嘗試寫入。

除了上述方案以外,還可以利用 ZNode 的有序性。在 C 下面建立多個有序的子節點。每當一個 Client 準備寫入數據的時候,創建一個臨時有序的節點。

節點的順序根據 FIFO 算法,保證先申請寫入的 Client 排在其前面。每個節點都有一個序號,后面申請的節點按照序號依次遞增。

ClientD,ClientE?分別建立子 ZNode

每個 Client 在執行修改 C 操作的時候,都要檢查有沒有比自己序號小的節點,如果存在那么就進入等待。

直到比自己序號小的節點進行完畢以后,才輪到自己執行修改操作。從而保證了事物處理的順序性。

會話(Session)

說完版本(Version)的概念,例子從原來的 ClientAB 已經擴充到了 ClientDE。這些客戶端都會和 ZooKeeper 的服務端進行通信,或讀取數據或修改數據。

我們將客戶端與服務端完成的這種連接稱為會話。ZooKeeper 的會話有 Connecting,Connected,Reconnecting,Reconnected 和 Close 這幾種狀態。

并且在服務端由專門的進程來管理他們,客戶端初始化的時候就會根據配置自動連接服務器,從而建立會話,客戶端連接服務器時會話處于 Connecting 狀態。

一旦連接完成,就會進入 Connected 狀態。如果出現延遲或者短暫失聯,客戶端會自動重連,Reconnecting 和 Reconnected 狀態也就應運而生。

如果長時間超時,或者客戶端斷開服務器,ZooKeeper 會清理掉會話,以及該會話創建的臨時數據節點,并且關閉和客戶端的連接。

Session 作為會話實體,用來代表客戶端會話,其包括 4 個屬性:

  • SessionID,用來全局唯一識別會話。

  • TimeOut,會話超時事件。客戶端在創造 Session 實例的時候,會設置一個會話超時的時間。

  • TickTime,下次會話超時時間點。后面“分桶策略”會用到。

  • isClosing,當服務端如果檢測到會話超時失效了,會通過設置這個屬性將會話關閉。

既然,會話是客戶端與服務器之間的連接。在服務器端由 SessionTracker 管理會話。

SessionTracker 有一個工作就是,將超時的會話清除掉。于是“分桶策略”就登場了。

由于每個會話在生成的時候都會定義超時時間,通過當前時間+超時時間可以算出會話的過期時間。

由于 SessionTracker 不是實時監聽會話超時,它是按照一定時間周期來監聽的。

也就是說,如果沒有到達 SessionTracker 的檢查時間周期,即使有會話過期,SessionTracker 也不會去清除。由此,就引入會話超時計算公式,也就是 TickTime 的計算公式。

TickTime=((當前時間+會話過期時間)/檢查時間間隔+1)*檢查時間間隔。

將這個值計算出來以后,SessionTracker 會把對應的會話按照這個時間放在對應的時間軸上面。SessionTracker 在對應的 TickTime 檢查會話是否過期。

計算會話下次的過期時間

每當客戶端連接上服務器都會做激活操作,同時每隔一段時間客戶端會向服務器發送心跳檢測。

服務器收到激活或者心跳檢測以后,會重新計算會話過期時間,根據“分桶策略”進行重新調整。把會話從“老的區塊“放到”新的區塊“中去。

重新計算過期時間并且調整“分桶策略”

對于超時的會話,SessionTracker 也會做如下清理工作:

  • 標記會話狀態為“已關閉”,也就是設置 isClosing 為 True。

  • 發起“會話關閉”的請求,讓關閉操作在整個集群生效。

  • 收集需要清理的臨時節點。

  • 添加“節點刪除”的事務變更。

  • 刪除臨時節點

  • 移除會話

  • 關閉客戶端與服務端的連接

會話關閉以后客戶端就無法從服務端獲取/寫入數據了。

服務群組(Leader,Follower,Observer)

前面提到了客戶端如何通過會話與服務端保持聯系,以及服務端是如何管理客戶端會話(Session)的。

我們繼續思考一下,這么多的服務端都依賴一個 ZooKeeper 服務器。一旦服務掛了,客戶端就無法工作了。

為了提高 ZooKeeper 服務的可靠性,引入服務器集群的概念。從原來的單個服務器,擴充成多個服務器,即使某一臺服務器掛了,其他的服務器也可以頂上來。

ZooKeeper 的服務器集群

這樣看起來不錯了,新的問題是,存在多個 ZooKeeper 服務器,那么客戶端的請求發給哪臺呢?服務器之間如何同步數據呢?如果一個服務掛掉了其他的服務器如何替代?這里介紹兩個概念 Leader 和 Follower。

Leader 服務器,是事務請求(寫操作)的唯一調度者和處理者,保證集群事務處理的順序性。也是集群內部服務器的調度者。

它是整個集群的老大,其他的服務器接到事務請求都會轉交給它,讓它協調處理。

Follower 服務器,處理非事務請求(讀操作),轉發事務請求給 Leader 服務器。參與選舉 Leader 的投票和事務請求 Proposal 的投票。

既然 Leader 是集群的老大,那么這個老大是如何產生的。ZooKeeper 有仲裁機制,通過服務器的選舉產生這個 Leader,按照少數服從多數的原則。

因此,集群中服務器的個數一般都是奇數,例如:1,3,5。當然這里是建議。關于選舉和仲裁都有一定的算法,一起來看看吧。

當眾多服務器啟動的時候,互相都不知道誰是 Leader,因此都會進入 Looking 狀態,也就是在網絡中尋找 Leader。

尋找的過程也是投票的過程,每個服務器會將服務器 ID 和事務 ID 作為投票信息發送給網絡中其他的服務器。假設稱它為投票信息 VOTE,它包括:(ServerID,ZXID)。

其中,ServerID 是服務器注冊的 ID,隨著服務器啟動的順序自動增加,后啟動的服務器 ServerID 就大;ZXID 是服務器處理事物的 ID,隨著事物的增加自動增加,同樣后提交的事務 ZXID 也大一些。

其他的服務器收到 VOTE 信息以后會和自己的 VOTE 信息(ServerID,ZXID)進行比較。

如果收到的 VOTE(ServerID,ZXID)中的 ZXID 比自己的 ZXID 要大,那么把自己的 VOTE 修改成收到的 VOTE。

如果 ZXID 一樣大,那么就比較 ServerID,將大的那個 ServerID 作為自己 VOTE 的 ServerID,轉發給其他服務器。

再簡單點說,如果事務 ID(ZXID)比自己的事務 ID(ZXID)要大,就把票投給這個服務器。如果事務 ID 一樣,就把票投給 ServerID 大的服務器。

來個具體的例子,有三個服務器,他們的投票值分別是:

  • S1 (1,6)

  • S2 (2,5)

  • S3 (3,5)

三個服務器分別把自己的 VOTE 發給其他兩臺服務器,S2 和 S3 收到 VOTE 以后發現 ZXID 為 6 的來自 S1 的 VOTE 比自己持有的 ZXID 要大,因此把自己的 VOTE 修改為(1,6)投出去,因此 S1 稱為 Leader。

Leader 選舉實例

同樣,如果 S1 作為 Leader,因為某種原因掛掉或者長時間沒有響應請求,其他的服務器也會進入 Looking 狀態,開啟投票仲裁模式尋找下一個 Leader。

成為新 Leader 以后會通過廣播的方式將 ZNode 上的數據同步到其他的 Follower。

Leader 有了,整個服務器集群有了領袖,它可以處理客戶端的事物請求。客戶端的請求可以發給集群中任意一臺服務器,無論是哪個服務器都會將事物請求轉交給 Leader。

Leader 在將數據寫入 ZNode 之前會向 ZooKeeper 的其他 Follower 進行廣播。

這里廣播用到了 ZAB 協議(Atomic?Broadcast?Protocol)是 Paxos 協議的實踐。說白了就是一個兩段提交。

PS:對分布式事務比較了解的同學應該知道兩段提交和三段提交。

這里 ZooKeeper 通過以下方式實現兩段提交:

  • Leader 向所有 Follower 發送一個 PROPOSAL。

  • 當 Follower 接收到 PROPOSAL 后,返回給 Leader 一個 ACK 消息,表示我收到 PROPOSAL,并且準備好了。

  • Leader 仲裁數量(過半數)的 Follower 發送的 ACK 后(包括 Leader 自己),會發送消息通知 Follower 進行 COMMIT。

  • 收到 COMMIT 以后,Follower 就開始干活,將數據寫入到 ZNode 中。

ZAB 廣播 PROPOSAL

選舉了 Leader 領導集群,Leader 接受到 Client 的請求以后,也可以協調 Follower 工作了。

那么如果 Client 很多的情況下,特別是這些客戶端都是做讀操作的時候,ZooKeeper 服務器如何處理如此多的請求呢?這里引入 Observer 的概念。

Observer 和 Follower 基本一致,對于非事務請求(讀操作),可以直接返回節點中的信息(數據從 Leader 中同步過來的)。

對于事務請求(寫操作),會轉交給 Leader 做統一處理。Observer 的存在就是為了解決大量客戶端讀請求。

Observer 和 Follower 的區別是,Observer 不參與仲裁投票,選舉 Leader。

Observer 加入 Leader 和 Follower 大家庭

總結

全文用了一個簡單的例子講 ZooKeeper 的主要特性和實現原理,最后做個總結。

ZooKeeper 被用來協調和管理分布式系統,發揮著重要的作用。分布式系統由于其特性,應用分布在不同的物理主機或者網絡中。

為了讓它們協同工作,ZooKeeper 中的 ZNode 成為統一協調的重要部分,客戶端通過 Client 間接到服務端的 ZNode 上,監聽 ZNode 數據的變化。

同時 ZNode 支持的持久,臨時和順序性,以及版本(Version)控制,這些特性支持了分布式事務和鎖的功能。

如果說,每一個 ZooKeeperClient 對 Server 的寫入操作都是一次事務的話,ZooKeeper 服務端維護了大量的事務,并且通過“分桶策略”來管理它們,保證了 Client 與 Server 端協調工作。

為了提高 Server 的可靠性,ZooKeeper 引入了 Server 集群的概念。通過仲裁機制選舉 Leader 來領導其他 Follower。

事物都由 Leader 來處理,通過兩段提交的方式對其他 Server 發起廣播。為了增強對非事務請求的處理效率,ZooKeeper 加入了 Observer 來幫忙。

ZooKeeper 包含的內容遠不止上面說的這些,由于篇幅的原因無法一一道來。

為了方便大家理解,文中將一些原理做了簡化處理,希望有機會和大家做深入的探討,咱們下次見。

《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

總結

以上是生活随笔為你收集整理的很遗憾,没有一篇文章能讲清楚ZooKeeper的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。