Zookeeper 允許客戶端向服務端的某個 Znode 注冊一個 Watcher 監聽,當服務端的一些指定事件觸發了這個 Watcher,服務端會向指定客戶端發送一個事件通知來實現分布式的通知功能,然后客戶端根據 Watcher 通知狀態和事件類型做出業務上的改變。
~
本篇內容包括:關于觀察者模式、Zookeeper 事件監聽和通知機制、Zookeeper 工作流程
文章目錄
- 一、關于觀察者模式
- 1、觀察者模式
- 2、發布-訂閱模式
- 3、Zookeeper 中的觀察者模式
- 二、Zookeeper 事件監聽和通知機制
- 1、Zookeeper Watcher 機制
- 2、Watcher 特性
- 三、Zookeeper 工作流程
- 1、客戶端注冊 Watcher
- 2、服務器處理 Watcher
- 3、客戶端回調 Watcher
一、關于觀察者模式
1、觀察者模式
觀察者模式定義了對象間的一種一對多的依賴關系,當一個對象的狀態發生改變時,所有依賴于它的對象都將得到通知,并自動更新。觀察者模式屬于行為型模式,行為型模式關注的是對象之間的通訊,觀察者模式就是觀察者和被觀察者之間的通訊。觀察者模式有一個別名叫“發布-訂閱模式”,或者說是“訂閱-發布模式”,訂閱者和訂閱目標是聯系在一起的,當訂閱目標發生改變時,逐個通知訂閱者。
- 作用:一個對象狀態改變給其他對象通知的問題,而且要考慮到易用和低耦合,保證高度的協作。
- 優點:
- 觀察者和被觀察者是抽象耦合的
- 建立一套觸發機制
- 缺點
- 如果一個被觀察者對象有很多的直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間。
- 如果在觀察者和觀察目標之間有循環依賴的話,觀察目標會觸發它們之間進行循環調用,可能導致系統崩潰。
- 觀察者模式沒有相應的機制讓觀察者知道所觀察的目標對象是怎么發生變化的,而僅僅只是知道觀察目標發生了變化。
2、發布-訂閱模式
發布-訂閱模式并不屬于 24 種基本的設計模式,起初只是觀察者模式的一個別稱,但是經過時間的沉淀,似乎他已經強大了起來,已經獨立于觀察者模式,成為另外一種不同的設計模式。
現在的發布訂閱模式中,稱為發布者的消息發送者不會將消息直接發送給訂閱者,這意味著發布者和訂閱者不知道彼此的存在。在發布者和訂閱者之間存在第三個組件,稱為消息代理或調度中心或中間件,它維持著發布者和訂閱者之間的聯系,過濾所有發布者傳入的消息并相應地分發它們給訂閱者。
- 作用:發布-訂閱模式是前端常用的一種設計模式,現在主流的 MVVM 框架,都大量使用了此設計模式,其主要作用有以下兩點:
- 可以實現模塊間通信
- 可以在一定程度上實現異步編程
- 優點:
- 支持簡單的廣播通信,當對象狀態發生改變時,會自動通知已經訂閱過的對象(我們日常工作中也經常使用到,比如我們的 ajax 請求,請求有 success 和 error 的回調函數,我們可以訂閱 ajax 的 success 和 error 事件。我們并不關心對象在異步運行的狀態,我們只關心 success 的時候或者 error 的時候我們要做點我們自己的事情就可以了~)。
- 發布者與訂閱者耦合性降低,發布者只管發布一條消息出去,它不關心這條消息如何被訂閱者使用,同時,訂閱者只監聽發布者的事件名,只要發布者的事件名不變,它不管發布者如何改變
- 缺點
- 創建訂閱者需要消耗一定的時間和內存。
- 雖然可以弱化對象之間的聯系,如果過度使用的話,反而使代碼不好理解及代碼不好維護等等。
3、Zookeeper 中的觀察者模式
zookeeper 從設計模式上來看,是一個基于觀察者模式設計(或者說“發布-訂閱模式”更為貼切)的分布式服務管理框架,它負責存儲和管理大家都關心的數據,然后接受觀察者的注冊,一旦這些數據的狀態發生了變化,Zookeeper 就將負責通知已經在 Zookeeper 上注冊的觀察者做出相應的反應,從而實現集群中類似 Master/slave 管理模式。
二、Zookeeper 事件監聽和通知機制
1、Zookeeper Watcher 機制
Zookeeper 允許客戶端向服務端的某個 Znode 注冊一個 Watcher 監聽,當服務端的一些指定事件觸發了這個 Watcher,服務端會向指定客戶端發送一個事件通知來實現分布式的通知功能,然后客戶端根據 Watcher 通知狀態和事件類型做出業務上的改變。
Watcher 基于 Zookeeper 上創建的 Znode 節點,可以對這些節點綁定監聽事件,比如可以監聽節點數據變更、節點刪除、子節點狀態變更等事件,通過這個事件機制,可以基于 Zookeeper 實現分布式鎖,發布訂閱(多個訂閱者同時監聽某一個主題對象,當這個主題對象自身狀態發生變化時,會通知所有訂閱者)等功能。
2、Watcher 特性
當數據發生變化的時候, zookeeper 會產生一個 watcher 事件,并且會發送到客戶端。但是客戶端只會收到一次通知。如果后續這個節點再次發生變化,那么之前設置 watcher 的客戶端不會再次收到消息。(Watcher 是一次性的操作,當然,可以通過循環監聽去達到永久監聽效果)。
- 一次性:watcher 是一次性的,一旦觸發就會被移除,再次使用時需要重新注冊;
- 客戶端順序回調:watcher 回調是順序串行執行的,只有回調后客戶端才能看到最新的數據狀態,一個 watcher 回調邏輯不應太多。以免影響其他回調 watcher 執行;
- 輕量級:WatchEvent 是最小的通信單位,結構上只包含通知狀態、事件類型和節點路徑,并不會告訴姐點變化的前后具體內容;
- 實效性:watcher 只有在當前 session 徹底失效時才會無效,若在 session 有效期內快速重連成功,則 watcher 依然存在,仍可接收到通知。
三、Zookeeper 工作流程
ZooKeeper 的 Watcher 機制,總的來說可以分為三個過程:客戶端注冊 Watcher、服務器處理 Watcher 和客戶端回調 Watcher 客戶端。
-
客戶端注冊 Watcher 到服務端;
-
服務端發生數據變更;
-
服務端通知客戶端數據變更;
-
客戶端回調 Watcher 處理變更應對邏輯;
1、客戶端注冊 Watcher
Zookeeper 中注冊 watcher的 接口大概有如下幾個:
- 建立 Zookeeper 連接時傳入的 watcher;
- 通過 gtData、exists、getChildren; 來設置watcher,而它們又各有同步和異步兩種形式。Zookeeper 的所有讀操作都可以設置 watch 監視點: getData, getChildren, exists. 寫操作則是不能設置監視點的。
監視有兩種類型:數據監視點和子節點監視點。創建、刪除或者設置znode都會觸發這些監視點。exists,getData 可以設置數據監視點。getChildren 可以設置子節點變化。而可能監測的事件類型有: None、NodeCreated、NodeDataChanged、NodeDeleted、NodeChildrenChanged。
None // 客戶端連接狀態發生變化的時候 會收到None事件NodeCreated // 節點創建事件 NodeDeleted // 節點刪除事件NodeDataChanged // 節點數據變化NodeChildrenChanged // 子節點被創建 刪除觸發該事件
# 客戶端注冊流程:
- 調用 getData()/getChildren()/exist()三個 API,傳入 Watcher 對象
- 標記請求 request,封裝 Watcher 到 WatchRegistration
- 封裝成 Packet 對象,發服務端發送 request
- 收到服務端響應后,將 Watcher 注冊到 ZKWatcherManager 中進行管理
- 請求返回,完成注冊。
2、服務器處理 Watcher
# 服務端接收 Watcher 并存儲
接收到客戶端請求,處理請求判斷是否需要注冊 Watcher,需要的話將數據節點的節點路徑和 ServerCnxn(ServerCnxn 代表一個客戶端和服務端的連接,實現了 Watcher 的 process 接口,此時可以看成一個 Watcher 對象)存儲在WatcherManager 的 WatchTable 和 watch2Paths 中去。
# Watcher 觸發
以服務端接收到 setData() 事務請求觸發 NodeDataChanged 事件為例:
- 封裝 WatchedEvent:將通知狀態(SyncConnected)、事件類型(NodeDataChanged)以及節點路徑封裝成一個 WatchedEvent 對象
- 查詢 Watcher:從 WatchTable 中根據節點路徑查找 Watcher
- 沒找到:說明沒有客戶端在該數據節點上注冊過 Watcher
- 找到:提取并從 WatchTable 和 Watch2Paths 中刪除對應 Watcher(從這里可以看出 Watcher 在服務端是一次性的,觸發一次就失效了)
# 調用 process 方法來觸發 Watcher
這里 process 主要就是通過 ServerCnxn 對應的 TCP 連接發送 Watcher 事件通知。
3、客戶端回調 Watcher
客戶端 SendThread 線程接收事件通知,交由 EventThread 線程回調 Watcher。