ZkClient
在使用ZooKeeper的Java客戶端時,經常需要處理幾個問題:重復注冊watcher、session失效重連、異常處理。
要解決上述的幾個問題,可以自己解決,也可以采用第三方的java客戶端來完成。這里就介紹一種常用的客戶端zkclient,目前已經運用到了很多項目中,知名的有Dubbo、Kafka、Helix。
ZKClient的設計

ZkClient的組件說明
從上述結構上看,IZKConnection是一個ZkClient與ZooKeeper之間的一個適配器。在代碼里直接使用的是ZKClient,其實質還是委托了zookeeper來處理了。
前面有一篇文章中,已經說了,使用ZooKeeper客戶端來注冊watcher有幾種方法:1、創建ZooKeeper對象時指定默認的Watcher,2、getData(),3、exists(),4、getchildren。其中getdata,exists注冊的是某個節點的事件處理器(watcher),getchildren注冊的是子節點的事件處理器(watcher)。而在ZKClient中,根據事件類型,分為了節點事件(數據事件)、子節點事件。對應的事件處理器則是IZKDataListener和IZKChildListener。另外加入了Session相關的事件和事件處理器。
ZkEventThread是專門用來處理事件的線程。
重要處理流程說明
啟動ZKClient
在創建ZKClient對象時,就完成了到ZooKeeper服務器連接的建立。具體過程是這樣的:

1、 啟動時,指定好connection string,連接超時時間,序列化工具等。
2、 創建並啟動eventThread,用於接收事件,並調度事件監聽器Listener的執行。
3、 連接到zookeeper服務器,同時將ZKClient自身作為默認的Watcher。
為節點注冊Watcher
ZooKeeper的三個方法:getData、getChildren、exists,ZKClient都提供了相應的代理方法。就拿exists來看:

可以看到,是否注冊watcher,由hasListeners(path)來決定的。

hasListeners就是看有沒有與該數據節點綁定的listener。
所以呢,默認情況下,都會自動的為指定的path注冊watcher,並且是默認的watcher(ZKClient)。怎么才能讓hasListeners判定值為true呢,也就是怎么才能為path綁定Listener呢?
ZKClient提供了訂閱功能:

一個新建的會話,只需要在取得響應的數據節點后,調用subscribteXxx就可以訂閱上相應的事件了。
ZooKeeper的變更操作
Zookeeper中提供的變更操作有:節點的創建、刪除,節點數據的修改。
創建操作,數據節點分為四種,ZKClient分別為他們提供了相應的代理:

刪除節點的操作:

修改節點數據的操作:

writeDataReturnStat():寫數據並返回數據的狀態。
updateDataSerialized():修改已序列化的數據。執行過程是:先讀取數據,然后使用DataUpdater對數據修改,最后調用writeData將修改后的數據發送給服務端。
客戶端處理變更
前面已經知道,ZKClient是默認的Watcher,並且在為各個數據節點注冊的Watcher都是這個默認的Watcher。那么該是如何將各種事件通知給相應的Listener呢?
處理過程大致可以概括為下面的步驟:
1、判斷變更類型:變更類型分為State變更、ChildNode變更(創建子節點、刪除子節點、修改子節點數據)、NodeData變更(創建指定node,刪除節點,節點數據變更)。
2、取出與path關聯的Listeners,並為每一個Listener創建一個ZKEvent,將ZkEvent交給ZkEventThread處理。
3、ZkEventThread線程,拿到ZkEvent后,只需要調用ZkEvent的run方法進行處理。
從這里也可以知道,具體的怎么如何調用Listener,還要依賴於ZkEvent的run()實現了。
序列化處理
ZooKeeper中,會涉及到序列化、反序列化的操作有兩種:getData、setData。在ZKClient中,分別用readData、writeData來替代了。
對於readData:先調用zookeeper的getData,然后進行使用ZKSerializer進行反序列化工作。
對於writeData:先使用ZKSerializer將對象序列化后,再調用zookeeper的setData。
ZkClient如何解決使用ZooKeeper客戶端遇到的問題的呢?
Watcher自動重注冊:這個要是依賴於hasListeners()的判斷,來決定是否再次注冊。如果對此有不清晰的,可以看上面的流程處理的說明
Session失效重連:如果發現會話過期,就先關閉已有連接,再重新建立連接。
異常處理:對比ZooKeeper和ZKClient,就可以發現ZooKeeper的所有操作都是拋異常的,而ZKClient的所有操作,都不會拋異常的。在發生異常時,它或做日志,或返回空,或做相應的Listener調用。
相比於ZooKeeper官方客戶端,使用ZKClient時,只需要關注實際的Listener實現即可。所以這個客戶端,還是推薦大家使用的。
