ZooKeeper源碼學習筆記--client端解析

2022-01-05 InfoQ 架構頭條

ZooKeeper是一個相對簡單的分布式協調服務,通過閱讀源碼我們能夠更進一步的清楚分布式的原理。

ZooKeeper 3.4.9

在bin/zkCli.sh中,我們看到client端的真實入口其實是一個org.apache.zookeeper.ZooKeeperMain的Java類

通過源碼走讀,看到在ZooKeeperMain中主要由兩部分構成

構造一個ZooKeeper對象,同ZooKeeperServer進行建立通信連接

通過反射調用jline.ConsoleReader類,對終端輸入進行讀取,然後通過解析單行命令,調用ZooKeeper接口。

如上所述,client端其實是對 zookeeper.jar 的簡單封裝,在構造出一個ZooKeeper對象後,通過解析用戶輸入,調用 ZooKeeper 接口和 Server 進行交互。

剛才我們看到 client 端同 ZooKeeper Server 之間的交互其實是通過 ZooKeeper 對象進行的,接下來我們詳細深入到 ZooKeeper 類中,看看其和服務端的交互邏輯。

在 ZooKeeper的構造方法中,可以看到 ZooKeeper 中使用 Server 的伺服器地址構建了一個 ClientCnxn 類,在這個類中,系統新建了兩個線程

其中,SendThread 負責將ZooKeeper的請求信息封裝成一個Packet,發送給 Server ,並維持同Server的心跳,EventThread負責解析通過通過SendThread得到的Response,之後發送給Watcher::processEvent進行詳細的事件處理。

 Client 時序圖

如上圖所示,Client中在終端輸入指令後,會被封裝成一個Request請求,通過submitRequest,進一步被封裝成Packet包,提交給SendThread處理。

SendThread通過doTransport將Packet發送給Server,並通過readResponse獲取結果,解析成一個Event,再將Event加入EventThread的隊列中等待執行。

EventThread通過processEvent消費隊列中的Event事件。

SendThread 的主要作用除了將Packet包發送給Server之外,還負責維持Client和Server之間的心跳,確保 session 存活。

現在讓我們從源碼出發,看看SendThread究竟是如何運行的。

SendThread是一個線程類,因此我們進入其run()方法,看看他的啟動流程。

從上面的代碼中,可以看出SendThread的主要任務如下:

創建同 Server 之間的 socket 連結

判斷連結是否超時

定時發送心跳任務

將ZooKeeper指令發送給Server

ZooKeeper通過獲取ZOOKEEPER_CLIENT_CNXN_SOCKET變量構造了一個ClientCnxnSocket對象,默認情況下是ClientCnxnSocketNIO類。

在ClientCnxnSocketNIO::connect中我們可以看到這裡同Server之間創建了一個socket連結。

在SendThread::run中,可以看到針對連結是否建立分別有readTimeout和connetTimeout 兩種超時時間,一旦發現連結超時,則拋出異常,終止 SendThread。

在沒有超時的情況下,如果判斷距離上次心跳時間超過了1/2個超時時間,會再次發送心跳數據,避免訪問超時。

在時序圖中,我們看到從終端輸入指令後,我們會將其解析成一個Packet 包,等待SendThread進行發送。

以ZooKeeper::create為例

在這裡create指令,被封裝成了一個 CreateRequest,通過submitRequest被轉成了一個Packet包

在submitRequest中,我們進一步看到Request被封裝成一個Packet包,並加入SendThread::outgoingQueue隊列中,等待執行。

Note: 在這裡我們還看到,ZooKeeper方法中所謂的同步方法其實就是在Packet被提交到SendThread之後,陷入一個while循環,等待處理完成後再跳出的過程

在SendThread::run的while循環中,ZooKeeper通過doTransport將存放在outgoingQueue中的Packet包發送給 Server。

在doIO發送socket信息之前,先從socket中獲取返回數據,通過readResonse進行處理。

在readReponse中,通過解析數據,我們可以得到WatchedEvent對象,並將其壓入EventThread的消息隊列,等待分發

在EventThread中通過processEvent對隊列中的事件進行消費,並分發給不同的Watcher

通常在ZooKeeper中,我們會為指定節點添加一個Watcher,用於監聽節點變化情況,以ZooKeeper:exist為例

代碼的大致邏輯和create類似,但是對wathcer做了一層ExistWatchRegistration的包裝,當packet對象完成請求之後,調用register方法,根據不同包裝的WatchRegistration將watch註冊到不同watch列表中,等待回調。

在 ZooKeeper 中一共有三種類型的WatchRegistration,分別對應DataWatchRegistration,ChildWatchRegistration,ExistWatchRegistration。 並在ZKWatchManager類中根據每種類型的WatchRegistration,分別有一張map表負責存放。

當EventThread::processEvent 時,根據event的所屬路徑,從三張map中獲取對應的watch列表進行消息通知及處理。

client 端的源碼分析就到此為止了。

ZooKeeper Client 的源碼很簡單,擁有三個獨立線程分別對命令進行處理,分發和響應操作,在保證各個線程相互獨立的基礎上,儘可能避免了多線程操作中出現鎖的情況。

相關焦點

  • client-go 源碼學習總結
    前言目前在雲原生社區的 Kubernetes 源碼研習社中和廣大學友們共同學習鄭東旭大佬的 Kubernetes 源碼剖析[1]這本書。當前正在開展第一期學習活動,第五章節 client-go 的學習。之所以從這一章節開始學習,主要是考慮到 client-go 在源碼中相對比較獨立,可以單獨閱讀。
  • ETCD源碼分析Client端啟動流程分析
    接下來我們正式進入源碼分析流程。ETCD Client啟動流程分析我們先看一段啟動代碼樣例: cli, err := clientv3.New(clientv3.Config{ Endpoints: exampleEndpoints(), DialTimeout: dialTimeout, }) if err !
  • ZooKeeper 源碼和實踐揭秘
    對於開發人員,ZooKeeper 是一個學習和實踐分布式組件的不錯的選擇。本文對 ZooKeeper 的源碼進行簡析,也會介紹 ZooKeeper 實踐經驗,希望能幫助到 ZooKeeper 初學者 。文章部分內容參考了一些網絡文章,已標註在末尾參考文獻中。
  • 從Linux源碼看Socket(TCP)Client端的Connect
    今天筆者就來從Linux源碼的角度看下Client端的Socket在進行Connect的時候到底做了哪些事情。由於篇幅原因,關於Server端的Accept源碼講解留給下一篇博客。(基於Linux 3.10內核)一個最簡單的Connect例子int clientSocket;if((clientSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) { // 創建socket失敗失敗 return -1;}.
  • 聊聊 Kafka:編譯 Kafka 源碼並搭建源碼環境
    這裡我也給出搭建的步驟,不管你是啥系統,都是類似的~3.4.1 下載wget http://mirrors.hust.edu.cn/apache/zookeeper/zookeeper-3.4.14/zookeeper-3.4.14.tar.gz3.4.2 解壓
  • Kubernetes 學習筆記之 ServiceAccount TokensController 源碼解析
    在 Kubernetes 學習筆記之 ServiceAccount AdmissionController 源碼解析 文章中,知道一個 ServiceAccount 對象都會引用一個type="kubernetes.io/service-account-token" 的 secret 對象,這個 secret
  • ZooKeeper基本原理你懂了麼?
    最終一致性:client不論連接到哪個Server,展示給它都是同一個視圖,這是zookeeper最重要的性能。2. 可靠性:具有簡單、健壯、良好的性能,如果消息m被到一臺伺服器接受,那麼它將被所有的伺服器接受。3. 實時性:Zookeeper保證客戶端將在一個時間間隔範圍內獲得伺服器的更新信息,或者伺服器失效的信息。
  • Kubernetes學習筆記之LRU算法源碼解析
    Kubernetes學習筆記之LRU算法源碼解析Overview本文章基於k8s release-1.17分支代碼。之前一篇文章學習 Kubernetes學習筆記之ServiceAccount TokensController源碼解析 ,主要學習ServiceAccount有關知識,發現其中使用了LRU Cache,代碼在 L106 。
  • OkHttp3源碼解析(整體流程)
    前言今天主要講一下OkHttp3
  • client-go實戰之二:RESTClient
    歡迎訪問我的GitHub這裡分類和匯總了欣宸的全部原創(含配套源碼):https://github.com/zq2599/
  • 從零開始學習大數據系列(六十二)HBase Client API使用入門
    [本文字數2500字左右,閱讀需要10-15分鐘,操作需要約30分鐘]我們已經學習HBase的原理及HBase的Shell操作,但是想要基於
  • Vue.js 3.x 源碼解析先導
    前言2018 年 6 月我在慕課網發布了 Vue.js 2.x 的源碼解析課程 《Vue.js 源碼全方位深入解析》,同時也開源了課程配套電子書。時隔一年多,Vue 官方也開源了 Vue.js 3.x,那麼在不久的將來,我也會系統化地做 Vue.js 3.x 的源碼分析,同時更新我的這門課程視頻以及電子書。
  • 學習 sentry 源碼架構,打造屬於自己的前端異常監控平臺
    整體架構這詞語好像有點大,姑且就算是源碼整體結構吧,主要就是學習是代碼整體結構,不深究其他不是主線的具體函數的實現。文章學習的是打包整合後的代碼,不是實際倉庫中的拆分的代碼。其餘三篇分別是:1.學習 jQuery 源碼整體架構,打造屬於自己的 js 類庫2.學習underscore源碼整體架構,打造屬於自己的函數式編程類庫3.學習 lodash 源碼整體架構,打造屬於自己的函數式編程類庫感興趣的讀者可以點擊閱讀。
  • 10.Go語言編程快速入門學習之HTTP Client/Server端的實現
    2.HTTP 客戶端3.綜合實踐3.1 Get 請求示例3.2 Post 請求示例3.3 PostForm 請求示例3.4 Http 服務響應示例0x01Go語言基礎之HTTP C/S實現描述: 在Socket網絡編程中我們學習TCP協議與UDP協議的服務端與客戶端代碼的編寫實踐, 今天我們來學習在應用層中我們使用最多的
  • BNC源碼閱讀筆記
    簡單介紹實時是 GNSS 衛星導航定位的重要應用場景。國際 GNSS 服務組織(IGS)早在2001年成立了實時工作組,從2007年開始啟動 Real-time Pilot 項目,旨在推動實時 GNSS 規範的制訂,軟體開發,國際合作與技術的發展。並於2013年4月1日正式上線了IGS RTS 服務。
  • 消息隊列 NSQ 源碼學習筆記 (一)
    nsqlookupd 的主要作用是維護和管理服務的拓撲結構,然後nsqd和消費端通過nsqlookupd服務來查詢響應的拓撲。配置文件採用toml格式,採用下包實現(文末講解了配置解析邏輯):github.com/mreiferson/go-options構造nsqlookupdRegistrationDB 的實現12345678
  • Tips:使交互式 Client 端自動化
    下文將以某客戶端(安全性考慮,不直接給出客戶端名稱)為示例,給出交互式 Client 端自動化實現相關腳本。示例假如有交互式客戶端:nothisclient。set -o noglobsource /etc/profilesource /root/.bash_profilesource /root/.bashrcnothisclient_abs_path='xxxc'# 轉義函數:將字符串中特殊符號轉義為對 expect 友好。
  • 百度大牛甩出筆記全新演繹Spring 5新特性,原理+源碼+實戰三飛
    它的重要性想必不用我多說了,作為一位身處於2020年的Java程式設計師,從源碼到實際開發,Spring這一塊是我們繞不過去的坎。既然繞不過去,那就啃下他!對於Spring的學習,小編前段時間剛好在百度的一位大佬手上拿到一份Spring進階寶典,看了之後,發現市面上的資料與其差距不止一點點!
  • zookeeper入門系列:概述
    在現今這個年代,介紹zookeeper的書和文章可謂多如牛毛,本人不才,試圖通過自己的理解來介紹zookeeper,希望通過一個初學者的視角來學習zookeeper,以期讓人更加深入和平穩的理解zookeeper。其中參考了不少教程和書,相關書目列在文末,也感謝這些作者。學習新的框架,先讓我們搞清楚他是什麼,這是它的內涵。