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 通知機制