最近開始對zookeeper源碼進行學習,本篇為zookeeper源碼學習的開篇,從整體上對zookeeper進行剖析.
一、ZooKeeper總體介紹
1.1、什么是zookeeper
ZooKeeper 是一個分布式的,開放源碼的分布式應用程序協同服務+存儲系統,同時是一款世界級的優秀開源產品,在大數據生態系統中 Hadoop、Storm、HBase、Spark、Flink、Kafka 隨處都是 ZooKeeper的應用場景。特別是在粗粒度分布式鎖、分布式選主、主備高可用切換等不需要高 TPS 的場景下有不可替代的作用。
1.2、ZooKeeper 應用場景
很多分布式協調服務都可以用 ZooKeeper 來做,其中典型應用場景如下:
1、配置管理:比如微服務系統,各個獨立服務都要使用集中化的配置管理,這個時候就需要 ZooKeeper。
2、組成員管理:比如上面講到的 HBase 其實就是用來做集群的組成員管理。
3、各種分布式鎖:ZooKeeper 適用于存儲和協同相關的關鍵數據,不適合用于大數據量存儲。如果要存 KV 或者大量的業務數據,還是要用數據庫或者其他 NoSql 來做。
4、注冊中心:大多數中小型公司都用zk來做注冊中心
至于為什么 ZooKeeper 不適合大數據量存儲呢?主要有以下兩個原因:
1、設計方面:ZooKeeper 需要把所有的數據(它的 data tree)加載到內存中。這就決定了ZooKeeper 存儲的數據量受內存的限制。一般的數據庫系統例如 MySQL可以存儲大于內存的數據,這是因為 InnoDB 是基于 B-Tree 的存儲引擎(基于內存+磁盤一致性)。
2、工程方面:ZooKeeper 的設計目標是為協同服務提供數據存儲,數據的高可用性和性能是最重要的系統指標,處理大數量不是 ZooKeeper 的首要目標。因此,ZooKeeper 不會對大數量存儲做太多工程上的優化。
二.ZooKeeper源碼環境
2.1、ZooKeeper版本選擇
在了解任何技術源碼的時候,最重要的兩件事要搞清楚:
1、版本如何選擇
2、源碼環境準備
zookeeper的大版本:
1、zookeeper-3.4.x 企業最常用,大數據技術組件最常用
2、zookeeper-3.5.x
3、zookeeper-3.6.x
最總結論:zookeeper-3.4.14.tar.gz,安裝包就是源碼包, ZooKeeper-3.5 以上,源碼 和 安裝包就分開了。
2.2、 ZooKeeper源碼環境準備
1、準備一個IDE:IDEA
2、從官網下載源碼包,IDEA去導入這個源碼項目即可
3、稍微等待一下,maven去下載一些依賴jar
4、從官網下載 zookeeper-3.4.14.tar.gz 安裝包,該安裝包直接包含源碼或者從 github 去拉取源碼項目
三.ZooKeeper基礎之序列化機制
3.1、序列化使用場景
1、當在網絡中需要進行消息,數據傳輸,那么這些數據就需要進行序列化和反序列化
2、當數據需要被持久化到磁盤的時候
3.2、什么是序列化, 為什么要進行序列化操作
1、序列化是指將我們定義好的 go/php/java 類型轉化成數據流的形式。之所以這么做是因為在網絡傳輸過程中,TCP 協議采用“流通信”的方式,提供了可以讀寫的字節流
2、任何一個分布式系統的底層,都必然會有網絡通信,這就必然要提供一個分布式通信框架和序列化機制。所以我們在看 ZooKeeper 源碼之前,先搞定 ZooKeeper 網絡通信和序列化。
3.3、序列化實現方式
3.3.1、java序列化實現
Java 中進行序列化和反序列化的過程中,主要使用 ObjectInputStream 和 ObjectOutputStream 來進行具體的序列化和反序列化。
3.3.2、hadoop的序列化實現
3.3.3、ZooKeeper 中的序列化機制
序列化的 API 主要在 zookeeper-jute 子項目中。
3.3.4、重點API:
org.apache.jute.InputArchive:反序列化需要實現的接口,其中各種 read 開頭的方法,都是反序列化方法
org.apache.jute.OutputArchive:所有進行序列化操作的都是實現這個接口,其中各種 write 開頭的方法都是序列化方法。
org.apache.jute.Index:用于迭代數據進行反序列化的迭代器
org.apache.jute.Record:在 ZooKeeper 要進行網絡通信的對象,都需要實現這個接口。里面有序列化和反序列化兩個重要的方法
四、ZooKeeper基礎之持久化機制
對于只要底層涉及到關于數據的存儲,讀寫操作, 一般都會有一個持久化機制來保證.那么ZooKeeper的數據模型主要涉及兩類知識:數據模型 和 持久化機制, 背后是兩套API來支撐
1、數據模型 : ZKDataBase + DataTree + DataNode
2、持久化機制: TxnLog + SnapLog
ZooKeeper 本身是一個對等架構(內部選舉,從所有 learner 中選舉一個 leader, 剩下的成為follower)
1、每個節點上都保存了整個系統的所有數據(leader存儲了數據,所有的follower節點都是leader的副本節點)
2、每個節點上的都把數據放在磁盤一份,放在內存一份, 保證磁盤跟內存一致性,來平衡讀寫性能
ZooKeeper的數據模型,抽象出了重要的三個API用來完成數據的管理:
1、ZKDataBase 負責管理 DataTree ,執行 DataTree 的相關 快照和恢復的操作
2、DataTree znode系統的完整抽象, 整個數據樹結構
3、DataNode znode 系統中的一個節點的抽象
關于 ZooKeeper 中的數據在內存中的組織,其實就是一棵樹:
1、這棵樹就叫做:DataTree (抽象了一棵樹)
2、這棵樹上的節點:DataNode (抽象一個節點)
3、關于管理這個 DataTree 的組件就是 ZKDataBase (內存數據庫:針對 DataTree 能做各種操作)
ZooKeeper 的持久化的一些操作接口,都在org.apache.zookeeper.server.persistence 包中。
主要的類的介紹:
第一組:主要是用來操作日志的(如果客戶端往zk中寫入一條數據,則記錄一條日志)
TxnLog,接口,讀取事務性日志的接口。
FileTxnLog,實現TxnLog接口,添加了訪問該事務性日志的API。
第二組:拍攝快照(當內存數據持久化到磁盤)
Snapshot,接口類型,持久層快照接口。
FileSnap,實現Snapshot接口,負責存儲、序列化、反序列化、訪問快照。
第三組;兩個成員變量:TxnLog和SnapShot
FileTxnSnapLog,封裝了TxnLog和SnapShot。
第四組:工具類
Util,工具類,提供持久化所需的API。
五、ZooKeeper基礎之網絡通信機制
Java IO 有幾個種類:
1、BIO JDK-1.1(編碼簡單,效率低) 阻塞模型
2、NIO JDK-1.4(效率有提升,編碼復雜) 基于reactor實現的異步非阻塞網絡通信模型 通常的IO的選擇:
1)、原生NIO 2)、基于NIO實現的網絡通信框架:netty
3、AIO JDK-1.7(效率最高,編碼復雜度一般) 真正的異步非阻塞通信模型
NIO 的三大API:
1、Buffer
2、Channel
3、Selector
ZooKeeper 中的通信有兩種方式:
1、NIO,默認使用NIO
2、Netty
兩個最重要的API:
ServerCnxn 服務端的通信組件
ClientCnxn 客戶端的通信組件
關于客戶端和服務端的一個定義:誰發請求,誰就是客戶端,誰接收和處理請求,誰就是服務端
1、真正的client給zookeeper發請求
2、zookeeper中的leader給follower發命令
3、zookeeper中的followe給leader發請求
ServerCnxn實現包:org.apache.zookeeper.server.ServerCnxn
詳細說明:
Stats,表示ServerCnxn上的統計數據。
Watcher,表示事件處理,監聽器
ServerCnxn,表示服務器連接,表示一個從客戶端到服務器的連接。
ClientCnxn,存在于客戶端用來執行通信的組件
NettyServerCnxn,基于Netty的連接的具體實現。
NIOServerCnxn,基于NIO的連接的具體實現。
六、Zookeeper基礎之Watcher工作機制
客戶端的 Watcher 注冊:
1、org.apache.zookeeper.ZooKeeper:客戶端基礎類、存儲了ClientCnxn和ZkWatcherManager
2、ZKWatchManager:ZooKeeper的內部類,實現了ClientWatchManager接口,主要用來存儲各種類型的Watcher,主要有三種:dataWatches、existWatches、childWatches以及一個默認的defaultWatcher
3、org.apache.zookeeper.ClientCnxn:與服務端的交互類,主要包含以下對象:LinkedListoutgoingQueue、SendThread 和 EventThread,其中outgoingQueue未待發送給服務端的Packet列表,SendThread線程負責和服務端進行請求交互,而EventThread線程則負責客戶端Watcher事件的回調執行
4、WatchRegistration:Zookeeper的內容類,包裝了Watcher和clientPath,并且負責Watcher的注冊
5、Packet:ClientCnxn的內部類,與Zookeeper服務端通信的交互類
兩條主線
1、實現主線:Watcher + WatchedEvent
2、管理主線:WatchManager(負責響watcher.process(watchedEvent)) + ZKWatchManager(負責注冊等相關管理)
組件說明:
Watcher,接口類型,其定義了process方法,需子類實現。
Event,接口類型,Watcher的內部類,無任何方法。
KeeperState,枚舉類型,Event的內部類,表示Zookeeper所處的狀態。
EventType,枚舉類型,Event的內部類,表示Zookeeper中發生的事件類型。
WatchedEvent,表示對ZooKeeper上發生變化后的反饋,包含了KeeperState和EventType。
ClientWatchManager,接口類型,表示客戶端的Watcher管理者,其定義了materialized方法,需子類實現。
ZKWatchManager,Zookeeper的內部類,繼承ClientWatchManager。
MyWatcher,ZooKeeperMain的內部類,繼承Watcher。
ServerCnxn,接口類型,繼承Watcher,表示客戶端與服務端的一個連接。
WatchManager,管理Watcher。
Watcher 主要工作流程:
1、用戶調用 Zookeeper 的 getData 方法,并將自定義的 Watcher 以參數形式傳入,該方法的作用主要是封裝請求,然后調用 ClientCnxn 的 submitRequest 方法提交請求
2、 ClientCnxn 在調用 submitRequest 提交請求時,會將 WatchRegistration(封裝了我們傳入的Watcher 和clientPath )以參數的形式傳入,submitRequest 方法主要作用是將信息封裝成Packet(ClientCnxn的內部類),并將封裝好的 Packet 加入到 ClientCnxn 的待發送列表中(LinkedList outgoingQueue)
3、 SendThread 線程不斷地從 outgoingQueue 取出未發送的 Packet 發送給客戶端并且將該 Packet加入pendingQueue (等待服務器響應的Packet列表)中,并通過自身的 readResponse 方法接收服務端的響應
4、SendThread 接收到客戶端的響應以后,會調用 ClientCnxn 的finishPacket 方法進行 Watcher方法的注冊
5、在 finishPacket 方法中,會取出 Packet 中的 WatchRegistration 對象,并調用其 register 方法,從ZKWatchManager 取出對應的 dataWatches、existWatches 或者 childWatches 其中的一個Watcher 集合,然后將自己的 Watcher 添加到該 Watcher 集合中。
未完待續,下篇接著繼續分析