最近開始對zookeeper源碼進行學(xué)習(xí),本篇為zookeeper源碼學(xué)習(xí)的開篇,從整體上對zookeeper進行剖析.
一、ZooKeeper總體介紹
1.1聚蝶、什么是zookeeper
ZooKeeper 是一個分布式的杰妓,開放源碼的分布式應(yīng)用程序協(xié)同服務(wù)+存儲系統(tǒng),同時是一款世界級的優(yōu)秀開源產(chǎn)品,在大數(shù)據(jù)生態(tài)系統(tǒng)中 Hadoop碘勉、Storm巷挥、HBase、Spark验靡、Flink倍宾、Kafka 隨處都是 ZooKeeper的應(yīng)用場景。特別是在粗粒度分布式鎖胜嗓、分布式選主高职、主備高可用切換等不需要高 TPS 的場景下有不可替代的作用。
1.2辞州、ZooKeeper 應(yīng)用場景
很多分布式協(xié)調(diào)服務(wù)都可以用 ZooKeeper 來做怔锌,其中典型應(yīng)用場景如下:
1、配置管理:比如微服務(wù)系統(tǒng)孙技,各個獨立服務(wù)都要使用集中化的配置管理产禾,這個時候就需要 ZooKeeper。
2牵啦、組成員管理:比如上面講到的 HBase 其實就是用來做集群的組成員管理亚情。
3、各種分布式鎖:ZooKeeper 適用于存儲和協(xié)同相關(guān)的關(guān)鍵數(shù)據(jù)哈雏,不適合用于大數(shù)據(jù)量存儲楞件。如果要存 KV 或者大量的業(yè)務(wù)數(shù)據(jù)衫生,還是要用數(shù)據(jù)庫或者其他 NoSql 來做。
4土浸、注冊中心:大多數(shù)中小型公司都用zk來做注冊中心
至于為什么 ZooKeeper 不適合大數(shù)據(jù)量存儲呢罪针?主要有以下兩個原因:
1、設(shè)計方面:ZooKeeper 需要把所有的數(shù)據(jù)(它的 data tree)加載到內(nèi)存中黄伊。這就決定了ZooKeeper 存儲的數(shù)據(jù)量受內(nèi)存的限制泪酱。一般的數(shù)據(jù)庫系統(tǒng)例如 MySQL可以存儲大于內(nèi)存的數(shù)據(jù),這是因為 InnoDB 是基于 B-Tree 的存儲引擎(基于內(nèi)存+磁盤一致性)还最。
2墓阀、工程方面:ZooKeeper 的設(shè)計目標(biāo)是為協(xié)同服務(wù)提供數(shù)據(jù)存儲,數(shù)據(jù)的高可用性和性能是最重要的系統(tǒng)指標(biāo)拓轻,處理大數(shù)量不是 ZooKeeper 的首要目標(biāo)斯撮。因此,ZooKeeper 不會對大數(shù)量存儲做太多工程上的優(yōu)化扶叉。
二.ZooKeeper源碼環(huán)境
2.1勿锅、ZooKeeper版本選擇
在了解任何技術(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)備一個IDE:IDEA
2瞒大、從官網(wǎng)下載源碼包,IDEA去導(dǎo)入這個源碼項目即可
3搪桂、稍微等待一下透敌,maven去下載一些依賴jar
4、從官網(wǎng)下載 zookeeper-3.4.14.tar.gz 安裝包踢械,該安裝包直接包含源碼或者從 github 去拉取源碼項目
三.ZooKeeper基礎(chǔ)之序列化機制
3.1酗电、序列化使用場景
1、當(dāng)在網(wǎng)絡(luò)中需要進行消息内列,數(shù)據(jù)傳輸撵术,那么這些數(shù)據(jù)就需要進行序列化和反序列化
2、當(dāng)數(shù)據(jù)需要被持久化到磁盤的時候
3.2话瞧、什么是序列化, 為什么要進行序列化操作
1嫩与、序列化是指將我們定義好的 go/php/java 類型轉(zhuǎn)化成數(shù)據(jù)流的形式寝姿。之所以這么做是因為在網(wǎng)絡(luò)傳輸過程中,TCP 協(xié)議采用“流通信”的方式划滋,提供了可以讀寫的字節(jié)流
2饵筑、任何一個分布式系統(tǒng)的底層,都必然會有網(wǎng)絡(luò)通信处坪,這就必然要提供一個分布式通信框架和序列化機制根资。所以我們在看 ZooKeeper 源碼之前,先搞定 ZooKeeper 網(wǎng)絡(luò)通信和序列化稻薇。
3.3嫂冻、序列化實現(xiàn)方式
3.3.1胶征、java序列化實現(xiàn)
Java 中進行序列化和反序列化的過程中塞椎,主要使用 ObjectInputStream 和 ObjectOutputStream 來進行具體的序列化和反序列化。
3.3.2睛低、hadoop的序列化實現(xiàn)
3.3.3案狠、ZooKeeper 中的序列化機制
序列化的 API 主要在 zookeeper-jute 子項目中。
3.3.4钱雷、重點API:
org.apache.jute.InputArchive:反序列化需要實現(xiàn)的接口骂铁,其中各種 read 開頭的方法,都是反序列化方法
org.apache.jute.OutputArchive:所有進行序列化操作的都是實現(xiàn)這個接口罩抗,其中各種 write 開頭的方法都是序列化方法拉庵。
org.apache.jute.Index:用于迭代數(shù)據(jù)進行反序列化的迭代器
org.apache.jute.Record:在 ZooKeeper 要進行網(wǎng)絡(luò)通信的對象,都需要實現(xiàn)這個接口套蒂。里面有序列化和反序列化兩個重要的方法
四钞支、ZooKeeper基礎(chǔ)之持久化機制
對于只要底層涉及到關(guān)于數(shù)據(jù)的存儲,讀寫操作, 一般都會有一個持久化機制來保證.那么ZooKeeper的數(shù)據(jù)模型主要涉及兩類知識:數(shù)據(jù)模型 和 持久化機制, 背后是兩套API來支撐
1、數(shù)據(jù)模型 : ZKDataBase + DataTree + DataNode
2操刀、持久化機制: TxnLog + SnapLog
ZooKeeper 本身是一個對等架構(gòu)(內(nèi)部選舉烁挟,從所有 learner 中選舉一個 leader, 剩下的成為follower)
1骨坑、每個節(jié)點上都保存了整個系統(tǒng)的所有數(shù)據(jù)(leader存儲了數(shù)據(jù)撼嗓,所有的follower節(jié)點都是leader的副本節(jié)點)
2、每個節(jié)點上的都把數(shù)據(jù)放在磁盤一份欢唾,放在內(nèi)存一份, 保證磁盤跟內(nèi)存一致性,來平衡讀寫性能
ZooKeeper的數(shù)據(jù)模型且警,抽象出了重要的三個API用來完成數(shù)據(jù)的管理:
1、ZKDataBase 負(fù)責(zé)管理 DataTree 礁遣,執(zhí)行 DataTree 的相關(guān) 快照和恢復(fù)的操作
2斑芜、DataTree znode系統(tǒng)的完整抽象, 整個數(shù)據(jù)樹結(jié)構(gòu)
3、DataNode znode 系統(tǒng)中的一個節(jié)點的抽象
關(guān)于 ZooKeeper 中的數(shù)據(jù)在內(nèi)存中的組織亡脸,其實就是一棵樹:
1押搪、這棵樹就叫做:DataTree (抽象了一棵樹)
2树酪、這棵樹上的節(jié)點:DataNode (抽象一個節(jié)點)
3、關(guān)于管理這個 DataTree 的組件就是 ZKDataBase (內(nèi)存數(shù)據(jù)庫:針對 DataTree 能做各種操作)
ZooKeeper 的持久化的一些操作接口大州,都在org.apache.zookeeper.server.persistence 包中续语。
主要的類的介紹:
第一組:主要是用來操作日志的(如果客戶端往zk中寫入一條數(shù)據(jù),則記錄一條日志)
TxnLog厦画,接口疮茄,讀取事務(wù)性日志的接口。
FileTxnLog根暑,實現(xiàn)TxnLog接口力试,添加了訪問該事務(wù)性日志的API。
第二組:拍攝快照(當(dāng)內(nèi)存數(shù)據(jù)持久化到磁盤)
Snapshot排嫌,接口類型畸裳,持久層快照接口。
FileSnap淳地,實現(xiàn)Snapshot接口怖糊,負(fù)責(zé)存儲、序列化颇象、反序列化伍伤、訪問快照。
第三組遣钳;兩個成員變量:TxnLog和SnapShot
FileTxnSnapLog扰魂,封裝了TxnLog和SnapShot。
第四組:工具類
Util蕴茴,工具類劝评,提供持久化所需的API。
五荐开、ZooKeeper基礎(chǔ)之網(wǎng)絡(luò)通信機制
Java IO 有幾個種類:
1付翁、BIO JDK-1.1(編碼簡單,效率低) 阻塞模型
2晃听、NIO JDK-1.4(效率有提升百侧,編碼復(fù)雜) 基于reactor實現(xiàn)的異步非阻塞網(wǎng)絡(luò)通信模型 通常的IO的選擇:
1)、原生NIO 2)能扒、基于NIO實現(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
兩個最重要的API:
ServerCnxn 服務(wù)端的通信組件
ClientCnxn 客戶端的通信組件
關(guān)于客戶端和服務(wù)端的一個定義:誰發(fā)請求,誰就是客戶端乎澄,誰接收和處理請求突硝,誰就是服務(wù)端
1、真正的client給zookeeper發(fā)請求
2置济、zookeeper中的leader給follower發(fā)命令
3解恰、zookeeper中的followe給leader發(fā)請求
ServerCnxn實現(xiàn)包:org.apache.zookeeper.server.ServerCnxn
詳細(xì)說明:
Stats,表示ServerCnxn上的統(tǒng)計數(shù)據(jù)浙于。
Watcher护盈,表示事件處理,監(jiān)聽器
ServerCnxn羞酗,表示服務(wù)器連接腐宋,表示一個從客戶端到服務(wù)器的連接。
ClientCnxn整慎,存在于客戶端用來執(zhí)行通信的組件
NettyServerCnxn脏款,基于Netty的連接的具體實現(xiàn)。
NIOServerCnxn裤园,基于NIO的連接的具體實現(xiàn)。
六剂府、Zookeeper基礎(chǔ)之Watcher工作機制
客戶端的 Watcher 注冊:
1拧揽、org.apache.zookeeper.ZooKeeper:客戶端基礎(chǔ)類、存儲了ClientCnxn和ZkWatcherManager
2腺占、ZKWatchManager:ZooKeeper的內(nèi)部類淤袜,實現(xiàn)了ClientWatchManager接口,主要用來存儲各種類型的Watcher衰伯,主要有三種:dataWatches铡羡、existWatches、childWatches以及一個默認(rèn)的defaultWatcher
3意鲸、org.apache.zookeeper.ClientCnxn:與服務(wù)端的交互類烦周,主要包含以下對象:LinkedListoutgoingQueue、SendThread 和 EventThread怎顾,其中outgoingQueue未待發(fā)送給服務(wù)端的Packet列表读慎,SendThread線程負(fù)責(zé)和服務(wù)端進行請求交互,而EventThread線程則負(fù)責(zé)客戶端Watcher事件的回調(diào)執(zhí)行
4槐雾、WatchRegistration:Zookeeper的內(nèi)容類夭委,包裝了Watcher和clientPath,并且負(fù)責(zé)Watcher的注冊
5募强、Packet:ClientCnxn的內(nèi)部類株灸,與Zookeeper服務(wù)端通信的交互類
兩條主線
1崇摄、實現(xiàn)主線:Watcher + WatchedEvent
2、管理主線:WatchManager(負(fù)責(zé)響watcher.process(watchedEvent)) + ZKWatchManager(負(fù)責(zé)注冊等相關(guān)管理)
組件說明:
Watcher慌烧,接口類型配猫,其定義了process方法,需子類實現(xiàn)杏死。
Event泵肄,接口類型,Watcher的內(nèi)部類淑翼,無任何方法腐巢。
KeeperState,枚舉類型玄括,Event的內(nèi)部類冯丙,表示Zookeeper所處的狀態(tài)。
EventType遭京,枚舉類型胃惜,Event的內(nèi)部類,表示Zookeeper中發(fā)生的事件類型哪雕。
WatchedEvent船殉,表示對ZooKeeper上發(fā)生變化后的反饋,包含了KeeperState和EventType斯嚎。
ClientWatchManager利虫,接口類型,表示客戶端的Watcher管理者堡僻,其定義了materialized方法糠惫,需子類實現(xiàn)。
ZKWatchManager钉疫,Zookeeper的內(nèi)部類硼讽,繼承ClientWatchManager。
MyWatcher牲阁,ZooKeeperMain的內(nèi)部類固阁,繼承Watcher。
ServerCnxn咨油,接口類型您炉,繼承Watcher,表示客戶端與服務(wù)端的一個連接役电。
WatchManager赚爵,管理Watcher。
Watcher 主要工作流程:
1、用戶調(diào)用 Zookeeper 的 getData 方法冀膝,并將自定義的 Watcher 以參數(shù)形式傳入唁奢,該方法的作用主要是封裝請求,然后調(diào)用 ClientCnxn 的 submitRequest 方法提交請求
2窝剖、 ClientCnxn 在調(diào)用 submitRequest 提交請求時麻掸,會將 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)以后诚隙,會調(diào)用 ClientCnxn 的finishPacket 方法進行 Watcher方法的注冊
5、在 finishPacket 方法中起胰,會取出 Packet 中的 WatchRegistration 對象久又,并調(diào)用其 register 方法,從ZKWatchManager 取出對應(yīng)的 dataWatches效五、existWatches 或者 childWatches 其中的一個Watcher 集合地消,然后將自己的 Watcher 添加到該 Watcher 集合中。
未完待續(xù),下篇接著繼續(xù)分析