【zookeeper之七】Zookeeper客戶端

加瓦一枚發表於2018-12-13

一、前言

  前篇部落格分析了Zookeeper的序列化和通訊協議,接著繼續學習客戶端,客戶端是開發人員使用Zookeeper最主要的途徑,很有必要弄懂客戶端是如何與服務端通訊的。

二、客戶端

  2.1 客戶端組成

  Zookeeper客戶端主要由如下核心部件構成。

  1. Zookeeper例項,客戶端入口。

  2. ClientWatchManager, 客戶端Watcher管理器。

  3. HostProvider,客戶端地址列表管理器。

  4. ClientCnxn,客戶端核心執行緒,內部包含了SendThread和EventThread兩個執行緒,SendThread為I/O執行緒,主要負責Zookeeper客戶端和伺服器之間的網路I/O通訊;EventThread為事件執行緒,主要負責對服務端事件進行處理。

  Zookeeper客戶端初始化與啟動環節,就是Zookeeper物件的例項化過程。客戶端在初始化和啟動過程中大體可以分為如下3個步驟

  1. 設定預設Watcher

  2. 設定Zookeeper伺服器地址列表

  3. 建立ClientCnxn。

  若在Zookeeper構造方法中傳入Watcher物件時,那麼Zookeeper就會將該Watcher物件儲存在ZKWatcherManager的defaultWatcher中,並作為整個客戶端會話期間的預設Watcher。

  2.2 會話的建立

  下圖表示了客戶端與服務端會話建立的整個過程,包括初始化階段(第一階段)、會話建立階段(第二階段)、響應處理階段(第三階段)三個階段。

  2.3 伺服器地址列表

  在例項化Zookeeper時,使用者傳入Zookeeper伺服器地址列表,如192.168.0.1:2181,192.168.0.2:2181,192.168.0.3:2181,此時,Zookeeper客戶端在連線伺服器的過程中,是如何從這個伺服器列表中選擇伺服器的呢?Zookeeper收到伺服器地址列表後,會解析出chrootPath和儲存伺服器地址列表。

  1. Chroot,每個客戶端可以設定自己的名稱空間,若客戶端設定了Chroot,此時,該客戶端對伺服器的任何操作都將被限制在自己的名稱空間下,如設定Choot為/app/X,那麼該客戶端的所有節點路徑都是以/app/X為根節點。

  2. 地址列表管理,Zookeeper使用StaticHostProvider打散伺服器地址(shuffle),並將伺服器地址形成一個環形迴圈佇列,然後再依次取出伺服器地址。

  2.4 網路I/O

  ClientCnxn是Zookeeper客戶端中負責維護客戶端與服務端之間的網路連線並進行一系列網路通訊的核心工作類,Packet是ClientCnxn內部定義的一個堆協議層的封裝,用作Zookeeper中請求和響應的載體。Packet包含了請求頭(requestHeader)、響應頭(replyHeader)、請求體(request)、響應體(response)、節點路徑(clientPath/serverPath)、註冊的Watcher(watchRegistration)等資訊,然而,並非Packet中所有的屬性都在客戶端與服務端之間進行網路傳輸,只會將requestHeader、request、readOnly三個屬性序列化,並生成可用於底層網路傳輸的ByteBuffer,其他屬性都儲存在客戶端的上下文中,不會進行與服務端之間的網路傳輸

  ClientCnxn維護著outgoingQueue(客戶端的請求傳送佇列)和pendingQueue(服務端響應的等待佇列),outgoingQueue專門用於儲存那些需要傳送到服務端的Packet集合,pendingQueue用於儲存那些已經從客戶端傳送到服務端的,但是需要等待服務端響應的Packet集合。

  在正常情況下,會從outgoingQueue中取出一個可傳送的Packet物件,同時生成一個客戶端請求序號XID並將其設定到Packet請求頭中去,然後序列化後再傳送,請求傳送完畢後,會立即將該Packet儲存到pendingQueue中,以便等待服務端響應返回後進行相應的處理。

  客戶端獲取到來自服務端的完整響應資料後,根據不同的客戶端請求型別,會進行不同的處理。

  1. 若檢測到此時客戶端尚未進行初始化,那麼說明當前客戶端與服務端之間正在進行會話建立,直接將接收的ByteBuffer序列化成ConnectResponse物件。

  2. 若當前客戶端已經處於正常會話週期,並且接收到服務端響應是一個事件,那麼將接收的ByteBuffer序列化成WatcherEvent物件,並將該事件放入待處理佇列中。

  3. 若是一個常規請求(Create、GetData、Exist等),那麼從pendingQueue佇列中取出一個Packet來進行相應處理。首先會檢驗響應中的XID來確保請求處理的順序性,然後再將接收到的ByteBuffer序列化成Response物件。

  SendThread是客戶端ClientCnxn內部的一個核心I/O排程執行緒,用於管理客戶端與服務端之間的所有網路I/O操作,在Zookeeper客戶端實際執行中,SendThread的作用如下:

  1. 維護了客戶端與服務端之間的會話生命週期(通過一定週期頻率內向服務端傳送PING包檢測心跳),如果會話週期內客戶端與服務端出現TCP連線斷開,那麼就會自動且透明地完成重連操作。

  2. 管理了客戶端所有的請求傳送和響應接收操作,其將上層客戶端API操作轉換成相應的請求協議併傳送到服務端,並完成對同步呼叫的返回和非同步呼叫的回撥。

  3. 將來自服務端的事件傳遞給EventThread去處理。

  EventThread是客戶端ClientCnxn內部的一個事件處理執行緒,負責客戶端的事件處理,並觸發客戶端註冊的Watcher監聽。EventThread中的watingEvents佇列用於臨時存放那些需要被觸發的Object,包括客戶端註冊的Watcher和非同步介面中註冊的回撥器AsyncCallback。同時,EventThread會不斷地從watingEvents中取出Object,識別具體型別(Watcher或AsyncCallback),並分別呼叫process和processResult介面方法來實現對事件的觸發和回撥。

三、總結

  本篇博文講解了客戶端的相關細節,內容較為簡單易懂,該模組知識會為之後分析原始碼打下好的基礎,謝謝各位園友觀看~  

出處:http://www.cnblogs.com/leesf456/ 

相關文章