在ZooKeeper服務(wù)器啟動(dòng)期間,首先會(huì)進(jìn)行數(shù)據(jù)初始化工作,用于將存儲(chǔ)在磁盤(pán)上的數(shù)據(jù)文件加載到ZooKeeper服務(wù)器內(nèi)存中。
?
初始化流程
數(shù)據(jù)初始化工作其實(shí)就是從磁盤(pán)中加載數(shù)據(jù)的過(guò)程,主要包括了從快照文件中加載快照數(shù)據(jù)的根據(jù)事務(wù)日志進(jìn)行數(shù)據(jù)訂正兩個(gè)過(guò)程。
1.初始化FileTxnSnapLog
FileTxnSnapLog是ZooKeeper事務(wù)日志和快照數(shù)據(jù)訪問(wèn)層,用于銜接上層業(yè)務(wù)與底層數(shù)據(jù)存儲(chǔ)。底層數(shù)據(jù)包括了事務(wù)日志和快照兩部分,因此FileTxnSnapLog內(nèi)部氛圍FileTxnLog和FileSnap的初始化,分別代表事務(wù)日志管理器和快照數(shù)據(jù)管理器的初始化。
2.初始化ZKDatabase
接下來(lái)就開(kāi)始構(gòu)建內(nèi)存數(shù)據(jù)庫(kù)ZKDatabase了。在初始化過(guò)程中,首先會(huì)構(gòu)建一個(gè)初始化的DataTree,同時(shí)將步驟1中初始化的FileTxnSnapLog交給ZKDatabase,以便于內(nèi)存數(shù)據(jù)庫(kù)能夠?qū)κ聞?wù)日志和快照數(shù)據(jù)進(jìn)行訪問(wèn)。
DataTree是ZooKeeper內(nèi)存模型的核心模型,簡(jiǎn)而言之就是一棵樹(shù),保存了ZooKeeper上的所有節(jié)點(diǎn)信息,在每個(gè)ZooKeeper服務(wù)器內(nèi)部都是單例。在ZKDatabase初始化的時(shí)候,DataTree也會(huì)進(jìn)行相應(yīng)的初始化工作——?jiǎng)?chuàng)建一些ZooKeeper的默認(rèn)節(jié)點(diǎn),包括/、/zookeeper、/zookeeper/quota三個(gè)節(jié)點(diǎn)的創(chuàng)建。
除了ZooKeeper的數(shù)據(jù)節(jié)點(diǎn),在ZKDatabase的初始化階段還會(huì)創(chuàng)建一個(gè)用于保存所有客戶端會(huì)話超時(shí)時(shí)間的記錄器:sessionsWithTimeouts——會(huì)話超時(shí)時(shí)間記錄器。
3.創(chuàng)建PlayBackListener監(jiān)聽(tīng)器
PlayBackListener監(jiān)聽(tīng)器主要用來(lái)接收事務(wù)應(yīng)用過(guò)程中的回調(diào)。在后面讀者會(huì)看到,在ZooKeeper數(shù)據(jù)恢復(fù)后期,會(huì)有一個(gè)事務(wù)訂正過(guò)程,在這個(gè)過(guò)程中會(huì)回調(diào)PlayBackListener監(jiān)聽(tīng)器來(lái)進(jìn)行對(duì)應(yīng)的數(shù)據(jù)訂正。
4.處理快照文件
完成內(nèi)存數(shù)據(jù)庫(kù)的初始化之后,ZooKeeper就開(kāi)始從磁盤(pán)中恢復(fù)數(shù)據(jù)了。在上文中我們已經(jīng)提到,每一個(gè)快照數(shù)據(jù)文件中都保存了ZooKeeper服務(wù)器近似全量的數(shù)據(jù),因此首先從這些快照文件開(kāi)始加載。
5.獲取最新的100個(gè)快照文件
ZooKeeper服務(wù)器運(yùn)行一段時(shí)間之后,磁盤(pán)上會(huì)保留許多快照文件。另外由于每次數(shù)據(jù)快照過(guò)程中,ZooKeeper都會(huì)將全量數(shù)據(jù)Dump到磁盤(pán)快照文件中,因此往往更新時(shí)間最晚的那個(gè)文件包含了最新的全量數(shù)據(jù)。那么是否我們只需要這個(gè)罪行的快照文件就可以了呢?在ZooKeeper的實(shí)現(xiàn)中,會(huì)獲取最新的之多100個(gè)快照文件。
6.解析快照文件
獲取到這之多100個(gè)文件之后,ZooKeeper會(huì)“逐個(gè)”進(jìn)行解析每個(gè)快照文件都是內(nèi)存數(shù)據(jù)序列化到磁盤(pán)的二進(jìn)制文件,因此在這里需要對(duì)其進(jìn)行反序列化,生成DataTree對(duì)象和sessionsWithTimeouts集合。同時(shí)在這個(gè)過(guò)程中,還會(huì)進(jìn)行文件的checkSum校驗(yàn)以確認(rèn)快照文件的正確性。
在“逐個(gè)”解析的過(guò)程中,如果正確性校驗(yàn)通過(guò)的話,呢么通常只會(huì)解析最新的那個(gè)快照文件。換句話說(shuō),只有當(dāng)最新的快照文件不可用的時(shí)候,才會(huì)逐個(gè)進(jìn)行解析,知道將這100個(gè)文件全部解析完成。如果將步驟4中獲取的所有快照文件都解析完成后還是無(wú)法完成恢復(fù)一個(gè)完整的DataTree和sessionWithTimeouts,則認(rèn)為無(wú)法從磁盤(pán)中加載數(shù)據(jù),服務(wù)器啟動(dòng)失敗。
7.獲取罪行的ZXID
完成6之后,就已經(jīng)基于開(kāi)招文件構(gòu)建了一個(gè)完整的DataTree實(shí)例和sessionsWithTimeouts集合了。此時(shí)根據(jù)這個(gè)快照文件的文件名就可以解析出一個(gè)最新的ZXID:zxid_for_snap,它代表了ZooKeeper開(kāi)始進(jìn)行數(shù)據(jù)快照的時(shí)刻。
8.處理事務(wù)日志
在經(jīng)過(guò)前面7處理后,此時(shí)ZooKeeper服務(wù)器內(nèi)存中已經(jīng)有了一份近似全量的數(shù)據(jù)了,開(kāi)始就要通過(guò)事務(wù)日志來(lái)更新增量數(shù)據(jù)了。
9.獲取所有zxid_for_snap之后提交的事務(wù)
到這里,我們已經(jīng)獲取到了快照數(shù)據(jù)的最新ZXID。ZooKeeper中數(shù)據(jù)的快照機(jī)制決定了快照文件中并非包含了所有的事務(wù)操作。蛋是未被包含在快照中的那部分事務(wù)操作是可以我替你故宮 數(shù)據(jù)訂正來(lái)實(shí)現(xiàn)的。因此這里我們只需要從事務(wù)日志中獲取所有ZXID比步驟7中得到的zxid_for_snap大的事務(wù)操作。
10.事務(wù)應(yīng)用
獲取到所有ZXID大于zxid_for_snap的事務(wù)后,將其逐個(gè)應(yīng)用到之前基于快照數(shù)據(jù)文件恢復(fù)出來(lái)的DataTree和sessionsWithTimeouts中去。在事務(wù)應(yīng)用的過(guò)程中,還有一個(gè)細(xì)節(jié)需要我們注意,每當(dāng)有一個(gè)事務(wù)被應(yīng)用到內(nèi)存數(shù)據(jù)庫(kù)中,ZooKeeper同時(shí)會(huì)回調(diào)PlayBackListener監(jiān)聽(tīng)器,將這一事務(wù)操作記錄轉(zhuǎn)換成Proposal,保存到ZKDatabase.committedLog中,以便Follower進(jìn)行快速同步。
11.獲取最新ZXID
待所有事務(wù)都被完整地應(yīng)用到內(nèi)存數(shù)據(jù)庫(kù)中,基本上就完成了數(shù)據(jù)的初始化過(guò)程,此時(shí)再次獲取一個(gè)ZXID,用來(lái)標(biāo)識(shí)上次服務(wù)器正常運(yùn)行時(shí)提交的最大事務(wù)ID。
12.校驗(yàn)epoch
epoch是ZooKeeper中一個(gè)非常特別的變量,其字面意思是“時(shí)代”,在ZooKeeper中,epoch標(biāo)識(shí)了當(dāng)前Leader周期。每次選舉產(chǎn)生一個(gè)新的Leader服務(wù)器之后,就會(huì)生成一個(gè)新的cpoch。在運(yùn)行期間集群中機(jī)器互相通信的過(guò)程中,都會(huì)帶上這個(gè)epoch一確保彼此在同一個(gè)Leader周期內(nèi)。
在完成數(shù)據(jù)加載后,ZooKeeper會(huì)從步驟11中確定的ZXID中解析出事務(wù)處理的Leader周期:epochOfZxid。同時(shí)會(huì)從磁盤(pán)的currentEpoch和acceptedEpoch文件中對(duì)去出上次記錄的最新的epoch值,進(jìn)行校驗(yàn)。
以上就是ZooKeeper服務(wù)器啟動(dòng)時(shí)期的數(shù)據(jù)初始化的全過(guò)程。
更多精彩內(nèi)容,歡迎關(guān)注微信公眾號(hào):Java小筆記(ijavanote)