ZooKeeper源碼分析系列一開篇基礎(chǔ)知識(shí)剖析
點(diǎn)擊關(guān)注公眾號(hào),Java干貨及時(shí)送達(dá)

一、ZooKeeper總體介紹
1.1、什么是zookeeper
ZooKeeper 是一個(gè)分布式的,開放源碼的分布式應(yīng)用程序協(xié)同服務(wù)+存儲(chǔ)系統(tǒng),同時(shí)是一款世界級(jí)的優(yōu)秀開源產(chǎn)品,在大數(shù)據(jù)生態(tài)系統(tǒng)中 Hadoop、Storm、HBase、Spark、Flink、Kafka 隨處都是 ZooKeeper的應(yīng)用場(chǎng)景。特別是在粗粒度分布式鎖、分布式選主、主備高可用切換等不需要高 TPS 的場(chǎng)景下有不可替代的作用。
1.2、ZooKeeper 應(yīng)用場(chǎng)景
很多分布式協(xié)調(diào)服務(wù)都可以用 ZooKeeper 來做,其中典型應(yīng)用場(chǎng)景如下:
1、配置管理:比如微服務(wù)系統(tǒng),各個(gè)獨(dú)立服務(wù)都要使用集中化的配置管理,這個(gè)時(shí)候就需要 ZooKeeper。
2、組成員管理:比如上面講到的 HBase 其實(shí)就是用來做集群的組成員管理。
3、各種分布式鎖:ZooKeeper 適用于存儲(chǔ)和協(xié)同相關(guān)的關(guān)鍵數(shù)據(jù),不適合用于大數(shù)據(jù)量存儲(chǔ)。如果要存 KV 或者大量的業(yè)務(wù)數(shù)據(jù),還是要用數(shù)據(jù)庫或者其他 NoSql 來做。
4、注冊(cè)中心:大多數(shù)中小型公司都用zk來做注冊(cè)中心
至于為什么 ZooKeeper 不適合大數(shù)據(jù)量存儲(chǔ)呢?主要有以下兩個(gè)原因:
1、設(shè)計(jì)方面:ZooKeeper 需要把所有的數(shù)據(jù)(它的 data tree)加載到內(nèi)存中。這就決定了ZooKeeper 存儲(chǔ)的數(shù)據(jù)量受內(nèi)存的限制。一般的數(shù)據(jù)庫系統(tǒng)例如 MySQL可以存儲(chǔ)大于內(nèi)存的數(shù)據(jù),這是因?yàn)?InnoDB 是基于 B-Tree 的存儲(chǔ)引擎(基于內(nèi)存+磁盤一致性)。
2、工程方面:ZooKeeper 的設(shè)計(jì)目標(biāo)是為協(xié)同服務(wù)提供數(shù)據(jù)存儲(chǔ),數(shù)據(jù)的高可用性和性能是最重要的系統(tǒng)指標(biāo),處理大數(shù)量不是 ZooKeeper 的首要目標(biāo)。因此,ZooKeeper 不會(huì)對(duì)大數(shù)量存儲(chǔ)做太多工程上的優(yōu)化。
二.ZooKeeper源碼環(huán)境
2.1、ZooKeeper版本選擇
在了解任何技術(shù)源碼的時(shí)候,最重要的兩件事要搞清楚:
1、版本如何選擇
2、源碼環(huán)境準(zhǔn)備
zookeeper的大版本:
1、zookeeper-3.4.x 企業(yè)最常用,大數(shù)據(jù)技術(shù)組件最常用
2、zookeeper-3.5.x
3、zookeeper-3.6.x
最總結(jié)論:zookeeper-3.4.14.tar.gz,安裝包就是源碼包, ZooKeeper-3.5 以上,源碼 和 安裝包就分開了。
2.2、 ZooKeeper源碼環(huán)境準(zhǔn)備
1、準(zhǔn)備一個(gè)IDE:IDEA
2、從官網(wǎng)下載源碼包,IDEA去導(dǎo)入這個(gè)源碼項(xiàng)目即可
3、稍微等待一下,maven去下載一些依賴jar
4、從官網(wǎng)下載 zookeeper-3.4.14.tar.gz 安裝包,該安裝包直接包含源碼或者從 github 去拉取源碼項(xiàng)目
三.ZooKeeper基礎(chǔ)之序列化機(jī)制
3.1、序列化使用場(chǎng)景
1、當(dāng)在網(wǎng)絡(luò)中需要進(jìn)行消息,數(shù)據(jù)傳輸,那么這些數(shù)據(jù)就需要進(jìn)行序列化和反序列化
2、當(dāng)數(shù)據(jù)需要被持久化到磁盤的時(shí)候
3.2、什么是序列化, 為什么要進(jìn)行序列化操作
1、序列化是指將我們定義好的 go/php/java 類型轉(zhuǎn)化成數(shù)據(jù)流的形式。之所以這么做是因?yàn)樵诰W(wǎng)絡(luò)傳輸過程中,TCP 協(xié)議采用“流通信”的方式,提供了可以讀寫的字節(jié)流
2、任何一個(gè)分布式系統(tǒng)的底層,都必然會(huì)有網(wǎng)絡(luò)通信,這就必然要提供一個(gè)分布式通信框架和序列化機(jī)制。所以我們?cè)诳?ZooKeeper 源碼之前,先搞定 ZooKeeper 網(wǎng)絡(luò)通信和序列化。
3.3、序列化實(shí)現(xiàn)方式
3.3.1、java序列化實(shí)現(xiàn)
Java 中進(jìn)行序列化和反序列化的過程中,主要使用 ObjectInputStream 和 ObjectOutputStream 來進(jìn)行具體的序列化和反序列化。

3.3.2、hadoop的序列化實(shí)現(xiàn)

3.3.3、ZooKeeper 中的序列化機(jī)制

序列化的 API 主要在 zookeeper-jute 子項(xiàng)目中。
3.3.4、重點(diǎn)API:

org.apache.jute.InputArchive:反序列化需要實(shí)現(xiàn)的接口,其中各種 read 開頭的方法,都是反序列化方法
org.apache.jute.OutputArchive:所有進(jìn)行序列化操作的都是實(shí)現(xiàn)這個(gè)接口,其中各種 write 開頭的方法都是序列化方法。
org.apache.jute.Index:用于迭代數(shù)據(jù)進(jìn)行反序列化的迭代器
org.apache.jute.Record:在 ZooKeeper 要進(jìn)行網(wǎng)絡(luò)通信的對(duì)象,都需要實(shí)現(xiàn)這個(gè)接口。里面有序列化和反序列化兩個(gè)重要的方法
四、ZooKeeper基礎(chǔ)之持久化機(jī)制
對(duì)于只要底層涉及到關(guān)于數(shù)據(jù)的存儲(chǔ),讀寫操作, 一般都會(huì)有一個(gè)持久化機(jī)制來保證.那么ZooKeeper的數(shù)據(jù)模型主要涉及兩類知識(shí):數(shù)據(jù)模型?和?持久化機(jī)制, 背后是兩套API來支撐
1、數(shù)據(jù)模型 : ZKDataBase + DataTree + DataNode
2、持久化機(jī)制: TxnLog + SnapLog
ZooKeeper 本身是一個(gè)對(duì)等架構(gòu)(內(nèi)部選舉,從所有 learner 中選舉一個(gè) leader, 剩下的成為follower)
1、每個(gè)節(jié)點(diǎn)上都保存了整個(gè)系統(tǒng)的所有數(shù)據(jù)(leader存儲(chǔ)了數(shù)據(jù),所有的follower節(jié)點(diǎn)都是leader的副本節(jié)點(diǎn))
2、每個(gè)節(jié)點(diǎn)上的都把數(shù)據(jù)放在磁盤一份,放在內(nèi)存一份, 保證磁盤跟內(nèi)存一致性,來平衡讀寫性能
ZooKeeper的數(shù)據(jù)模型,抽象出了重要的三個(gè)API用來完成數(shù)據(jù)的管理:
1、ZKDataBase 負(fù)責(zé)管理 DataTree ,執(zhí)行 DataTree 的相關(guān) 快照和恢復(fù)的操作
2、DataTree znode系統(tǒng)的完整抽象, 整個(gè)數(shù)據(jù)樹結(jié)構(gòu)
3、DataNode znode 系統(tǒng)中的一個(gè)節(jié)點(diǎn)的抽象
關(guān)于 ZooKeeper 中的數(shù)據(jù)在內(nèi)存中的組織,其實(shí)就是一棵樹:
1、這棵樹就叫做:DataTree (抽象了一棵樹)
2、這棵樹上的節(jié)點(diǎn):DataNode (抽象一個(gè)節(jié)點(diǎn))
3、關(guān)于管理這個(gè) DataTree 的組件就是 ZKDataBase (內(nèi)存數(shù)據(jù)庫:針對(duì) DataTree 能做各種操作)
ZooKeeper 的持久化的一些操作接口,都在org.apache.zookeeper.server.persistence 包中。



主要的類的介紹:
第一組:主要是用來操作日志的(如果客戶端往zk中寫入一條數(shù)據(jù),則記錄一條日志)
TxnLog,接口,讀取事務(wù)性日志的接口。
FileTxnLog,實(shí)現(xiàn)TxnLog接口,添加了訪問該事務(wù)性日志的API。
第二組:拍攝快照(當(dāng)內(nèi)存數(shù)據(jù)持久化到磁盤)
Snapshot,接口類型,持久層快照接口。
FileSnap,實(shí)現(xiàn)Snapshot接口,負(fù)責(zé)存儲(chǔ)、序列化、反序列化、訪問快照。
第三組;兩個(gè)成員變量:TxnLog和SnapShot
FileTxnSnapLog,封裝了TxnLog和SnapShot。
第四組:工具類
Util,工具類,提供持久化所需的API。
五、ZooKeeper基礎(chǔ)之網(wǎng)絡(luò)通信機(jī)制
Java IO 有幾個(gè)種類:
1、BIO JDK-1.1(編碼簡單,效率低) 阻塞模型
2、NIO JDK-1.4(效率有提升,編碼復(fù)雜) 基于reactor實(shí)現(xiàn)的異步非阻塞網(wǎng)絡(luò)通信模型 通常的IO的選擇:
1)、原生NIO 2)、基于NIO實(shí)現(xiàn)的網(wǎng)絡(luò)通信框架:netty
3、AIO JDK-1.7(效率最高,編碼復(fù)雜度一般) 真正的異步非阻塞通信模型
NIO 的三大API:
1、Buffer
2、Channel
3、Selector
ZooKeeper 中的通信有兩種方式:
1、NIO,默認(rèn)使用NIO
2、Netty
兩個(gè)最重要的API:
ServerCnxn 服務(wù)端的通信組件
ClientCnxn 客戶端的通信組件
關(guān)于客戶端和服務(wù)端的一個(gè)定義:誰發(fā)請(qǐng)求,誰就是客戶端,誰接收和處理請(qǐng)求,誰就是服務(wù)端
1、真正的client給zookeeper發(fā)請(qǐng)求
2、zookeeper中的leader給follower發(fā)命令
3、zookeeper中的followe給leader發(fā)請(qǐng)求
ServerCnxn實(shí)現(xiàn)包:org.apache.zookeeper.server.ServerCnxn

ServerCnxn實(shí)現(xiàn)包
詳細(xì)說明:
Stats,表示ServerCnxn上的統(tǒng)計(jì)數(shù)據(jù)。
Watcher,表示事件處理,監(jiān)聽器
ServerCnxn,表示服務(wù)器連接,表示一個(gè)從客戶端到服務(wù)器的連接。
ClientCnxn,存在于客戶端用來執(zhí)行通信的組件
NettyServerCnxn,基于Netty的連接的具體實(shí)現(xiàn)。
NIOServerCnxn,基于NIO的連接的具體實(shí)現(xiàn)。
六、Zookeeper基礎(chǔ)之Watcher工作機(jī)制
客戶端的 Watcher 注冊(cè):
1、org.apache.zookeeper.ZooKeeper:客戶端基礎(chǔ)類、存儲(chǔ)了ClientCnxn和ZkWatcherManager
2、ZKWatchManager:ZooKeeper的內(nèi)部類,實(shí)現(xiàn)了ClientWatchManager接口,主要用來存儲(chǔ)各種類型的Watcher,主要有三種:dataWatches、existWatches、childWatches以及一個(gè)默認(rèn)的defaultWatcher
3、org.apache.zookeeper.ClientCnxn:與服務(wù)端的交互類,主要包含以下對(duì)象:LinkedListoutgoingQueue、SendThread 和 EventThread,其中outgoingQueue未待發(fā)送給服務(wù)端的Packet列表,SendThread線程負(fù)責(zé)和服務(wù)端進(jìn)行請(qǐng)求交互,而EventThread線程則負(fù)責(zé)客戶端Watcher事件的回調(diào)執(zhí)行
4、WatchRegistration:Zookeeper的內(nèi)容類,包裝了Watcher和clientPath,并且負(fù)責(zé)Watcher的注冊(cè)
5、Packet:ClientCnxn的內(nèi)部類,與Zookeeper服務(wù)端通信的交互類

兩條主線
1、實(shí)現(xiàn)主線:Watcher + WatchedEvent
2、管理主線:WatchManager(負(fù)責(zé)響應(yīng)watcher.process(watchedEvent)) + ZKWatchManager(負(fù)責(zé)注冊(cè)等相關(guān)管理)

組件說明:
Watcher,接口類型,其定義了process方法,需子類實(shí)現(xiàn)。
Event,接口類型,Watcher的內(nèi)部類,無任何方法。
KeeperState,枚舉類型,Event的內(nèi)部類,表示Zookeeper所處的狀態(tài)。
EventType,枚舉類型,Event的內(nèi)部類,表示Zookeeper中發(fā)生的事件類型。
WatchedEvent,表示對(duì)ZooKeeper上發(fā)生變化后的反饋,包含了KeeperState和EventType。
ClientWatchManager,接口類型,表示客戶端的Watcher管理者,其定義了materialized方法,需子類實(shí)現(xiàn)。
ZKWatchManager,Zookeeper的內(nèi)部類,繼承ClientWatchManager。
MyWatcher,ZooKeeperMain的內(nèi)部類,繼承Watcher。
ServerCnxn,接口類型,繼承Watcher,表示客戶端與服務(wù)端的一個(gè)連接。
WatchManager,管理Watcher。
Watcher 主要工作流程:
1、用戶調(diào)用 Zookeeper 的 getData 方法,并將自定義的 Watcher 以參數(shù)形式傳入,該方法的作用主要是封裝請(qǐng)求,然后調(diào)用 ClientCnxn 的 submitRequest 方法提交請(qǐng)求
2、 ClientCnxn 在調(diào)用 submitRequest 提交請(qǐng)求時(shí),會(huì)將 WatchRegistration(封裝了我們傳入的Watcher 和clientPath )以參數(shù)的形式傳入,submitRequest 方法主要作用是將信息封裝成Packet(ClientCnxn的內(nèi)部類),并將封裝好的 Packet 加入到 ClientCnxn 的待發(fā)送列表中(LinkedList outgoingQueue)
3、 SendThread 線程不斷地從 outgoingQueue 取出未發(fā)送的 Packet 發(fā)送給客戶端并且將該 Packet加入pendingQueue (等待服務(wù)器響應(yīng)的Packet列表)中,并通過自身的 readResponse 方法接收服務(wù)端的響應(yīng)
4、SendThread 接收到客戶端的響應(yīng)以后,會(huì)調(diào)用 ClientCnxn 的finishPacket 方法進(jìn)行 Watcher方法的注冊(cè)
5、在 finishPacket 方法中,會(huì)取出 Packet 中的 WatchRegistration 對(duì)象,并調(diào)用其 register 方法,從ZKWatchManager 取出對(duì)應(yīng)的 dataWatches、existWatches 或者 childWatches 其中的一個(gè)Watcher 集合,然后將自己的 Watcher 添加到該 Watcher 集合中。
往 期 推 薦
1、致歉!抖音Semi Design承認(rèn)參考阿里Ant Design
2、對(duì)比7種分布式事務(wù)方案,還是偏愛阿里開源的Seata,真香!
點(diǎn)分享
點(diǎn)收藏
點(diǎn)點(diǎn)贊
點(diǎn)在看





