zookeeper-watcher異步調用子服務

zookeeper=文件系統+通知機制

一、 文件系統

Zookeeper維護一個類似文件系統的數據結構:

每個子目錄項如 NameService 都被稱作爲 znode,和文件系統一樣,我們能夠自由的增加、刪除znode,在一個znode下增加、刪除子znode,唯一的不同在於znode是可以存儲數據的。

有四種類型的znode:

1)PERSISTENT-持久目錄節點

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

2) PERSISTENT_SEQUENTIAL-持久順序節點

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

3)EPHEMERAL-臨時目錄節點

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

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

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

 

二、 通知機制

客戶端註冊監聽它關心的目錄節點,當目錄節點發生變化(數據改變、被刪除、子目錄節點增加刪除)時,zookeeper會通知客戶端。

我們可以自定義Watcher,如果是Boolean型變量,當爲true時,則使用系統默認的Watcher,系統默認的Watcher是在Zookeeper的構造函數中定義的Watcher。參數中Watcher爲空或者false,表示不啓用Wather。

1、一次性觸發器

客戶端在Znode設置了Watch時,如果Znode內容發生改變,那麼客戶端就會獲得Watch事件。例如:客戶端設置getData("/znode1", true)後,如果/znode1發生改變或者刪除,那麼客戶端就會得到一個/znode1的Watch事件,但是/znode1再次發生變化,那客戶端是無法收到Watch事件的,除非客戶端設置了新的Watch。

 

2、發送至客戶端

Watch事件是異步發送到Client。Zookeeper可以保證客戶端發送過去的更新順序是有序的。例如:某個Znode沒有設置watcher,那麼客戶端對這個Znode設置Watcher發送到集羣之前,該客戶端是感知不到該Znode任何的改變情況的。換個角度來解釋:由於Watch有一次性觸發的特點,所以在服務器端沒有Watcher的情況下,Znode的任何變更就不會通知到客戶端。不過,即使某個Znode設置了Watcher,且在Znode有變化的情況下通知到了客戶端,但是在客戶端接收到這個變化事件,但是還沒有再次設置Watcher之前,如果其他客戶端對該Znode做了修改,這種情況下,Znode第二次的變化客戶端是無法收到通知的。這可能是由於網絡延遲或者是其他因素導致,所以我們使用Zookeeper不能期望能夠監控到節點每次的變化。Zookeeper只能保證最終的一致性,而無法保證強一致性。

 

3、設置watch的數據內容

Znode改變有很多種方式,例如:節點創建,節點刪除,節點改變,子節點改變等等。Zookeeper維護了兩個Watch列表,一個節點數據Watch列表,另一個是子節點Watch列表。getData()和exists()設置數據Watch,getChildren()設置子節點Watch。兩者選其一,可以讓我們根據不同的返回結果選擇不同的Watch方式,getData()和exists()返回節點的內容,getChildren()返回子節點列表。因此,setData()觸發內容Watch,create()觸發當前節點的內容Watch或者是其父節點的子節點Watch。delete()同時觸發父節點的子節點Watch和內容Watch,以及子節點的內容Watch。

 

 

 

Zookeeper Watcher的運行機制

1,Watch是輕量級的,其實就是本地JVM的Callback,服務器端只是存了是否有設置了Watcher的布爾類型。(源碼見:org.apache.zookeeper.server.FinalRequestProcessor)

2,在服務端,在FinalRequestProcessor處理對應的Znode操作時,會根據客戶端傳遞的watcher變量,添加到對應的ZKDatabase(org.apache.zookeeper.server.ZKDatabase)中進行持久化存儲,同時將自己NIOServerCnxn做爲一個Watcher callback,監聽服務端事件變化

3,Leader通過投票通過了某次Znode變化的請求後,然後通知對應的Follower,Follower根據自己內存中的zkDataBase信息,發送notification信息給zookeeper客戶端。

4,Zookeeper客戶端接收到notification信息後,找到對應變化path的watcher列表,挨個進行觸發回調

 

 

watcher分類:

defaultWatcher

existWatcher-->exist()

childWatcher-->getChildren()

dataWatcher-->getData()

 

watcher原理:

1、創建zookeeper實例的時候接收一個watcher參數,賦值給watchMnanger.defaultWatcher,此watcher與其它watcher不同,主要用於響應與鏈接狀態轉換有關的事件(建立鏈接,關閉鏈接等)

2、ZKWatchManager是客戶端watcher管理器,負責跟蹤多種watcher,每種類型的watcher將會被存在各自的Map中,key爲path,value爲Set<Watcher>。watcher由ZKWatchManager負責管理,並不會隨請求發送給server,而只會發給server此請求類型是否註冊了watch

3、在ZKDatabase中,包括一個DataTree,此dataTree持有對nodes以及相關的watcher的數據,server端,WatcherManager是管理client註冊的watcher,其數據結構爲HashSet<path,Set<Watcher>>

4、請求到達server之後,在FinalRequestProcessor中,將會處理各種請求,如果檢測到request.getWatch()爲true,即請求要求註冊watch,那麼將會把ServerCnxn和path關聯起來,加入到WatherManager相應的列表中.

5、客戶端的請求響應之後,由SendThread.readResponse()處理響應,如果響應code爲成功且此請求中註冊了watch,那麼將會把此wath添加到響應的watch列表中。

6、DataTree持有2個WatchManager對象,分別爲dataWatches用於管理註冊data操作的watch,childWatches用於管理註冊child操作的watch。

Zookeeper的Watcher機制主要包括客戶端線程、客戶端WatchManager和Zookeeper服務器三部分。在具體的流程上,客戶端向Zookeeper服務器註冊Watcher事件監聽的同時,會將Watcher對象存儲在 客戶端WatchManager中。當Zookeeper服務器觸發Watcher事件後,會向客戶端發送通知,客戶端線程從WatchManager中取出對應的Watcher對象執行回調邏輯(process方法)。

流程:

1、不同子服務在zk上註冊同一個znode節點

2、第一個子服務在節點上創建數據

3、第二個子服務監聽節點的變化

4、當節點改變時做出對應的操作

方法一:watche機制回調

創建的是持久節點

需要自己將傳遞的參數由json轉化爲String

子服務定時調用getData方法,判斷是否執行api

一個外部調用對應一個監聽

--缺點:每一個接口對應一個定時任務

 

方法二:zk異步調用api

創建的是臨時節點

可以直接傳遞json

主線程需要阻塞等待其他子服務的調用api

按create,exits,delete監聽

--每種類型的回調方法只有一個,需要滿足不同子服務的調用

 

原因

在調用一個接口的時候,可能會執行一些不必要的操作,有時這些操作等待響應的時間過長,如果同步調用接口,會使當前線程阻塞,大大影響了用戶體驗,所以利用zookeeper的watch機制,異步處理這些操作。

 

原理

1、Zookeeper提供瞭如下幾種可以"註冊watch"的操作:exist,getChildren,getData;而對於create,setData,delete是有可能觸發"watcher"的操作

2、zookeeper的節點大小爲1M

 

過程

1、創建子服務zookeeper,用於創建節點和對節點進行監聽

2、啓動服務的同時啓動監聽線程,創建定時任務讀取zookeeper節點

3、其他子服務執行時調用zookeeper服務,創建一個持久時序節點

4、線程定時獲取接口對應的子節點,根據子節點中的參數執行相應接口

5、讀取數據之後刪除節點

 

 

問題

1、其他子節點的session過期未被調用也會拋出異常?但是不影響自身節點的使用

org.apache.zookeeper.KeeperException$SessionExpiredException: KeeperErrorCode = Session expired for /esque2

/* 2、創建根節點下的子節點時會拋根節點不爲空的異常?但是不影響子節點的創建

org.apache.zookeeper.KeeperException$NotEmptyException: KeeperErrorCode = Directory not empty for /confTest */(因爲調用了delete方法去刪除根節點)

/*3、多個線程同時對一個子服務的節點進行修改, 會對其產生影響*/(創建持久順序節點)

4、如果寫操作創建了臨時節點,釋放之後讀操作無法獲取

5、接口回調方法的執行依賴於主線程的阻塞時間,如果在回調方法中調用其他子服務的接口,會執行了一半之後停止

 

 

https://blog.csdn.net/fayeyiwang/article/details/54743201 Zookeeper-Watcher機制與異步調用原理

https://blog.csdn.net/hohoo1990/article/details/78617336 zookeeper 中 Watcher 通知機制

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章