四、ZooKeeper技術(shù)內(nèi)幕

本文將從系統(tǒng)模型、序列化與協(xié)議、客戶端工作原理、會(huì)話、服務(wù)端工作原理以及數(shù)據(jù)存儲(chǔ)等方面來揭示ZooKeeper的技術(shù)內(nèi)幕。

一、系統(tǒng)模型

1.1 數(shù)據(jù)模型

ZooKeeper的視圖結(jié)構(gòu)使用了其特有的“數(shù)據(jù)節(jié)點(diǎn)”概念,我們稱之為ZNode。ZNode是ZooKeeper中數(shù)據(jù)的最小單元,每個(gè)ZNode上都可以保存數(shù)據(jù),同時(shí)還可以掛載子節(jié)點(diǎn),因此構(gòu)成了一個(gè)層次化的命名空間,我們稱之為樹。

1.2 節(jié)點(diǎn)特性

我們已知,ZooKeeper的命名空間是由一系列數(shù)據(jù)節(jié)點(diǎn)組成的,我們將對(duì)數(shù)據(jù)節(jié)點(diǎn)做詳細(xì)講解。

節(jié)點(diǎn)類型

在ZooKeeper中,每個(gè)數(shù)據(jù)節(jié)點(diǎn)都是有生命周期的,其生命周期的長(zhǎng)短取決于數(shù)據(jù)節(jié)點(diǎn)的節(jié)點(diǎn)類型。在ZooKeeper中,節(jié)點(diǎn)類型可以分為持久節(jié)點(diǎn)(PERSISTENT)、臨時(shí)節(jié)點(diǎn)(EPHEMERAL)和順序節(jié)點(diǎn)(SEQUENTIAL)三大類,ju'ti具體在節(jié)點(diǎn)創(chuàng)建過程中,通過組合使用,可以生成以下四種組合型節(jié)點(diǎn)類型:

  • 持久節(jié)點(diǎn)(PERSISTENT)

數(shù)據(jù)節(jié)點(diǎn)被創(chuàng)建后,就會(huì)一直存在于ZooKeeper服務(wù)器上,直到有刪除操作來主動(dòng)清除這個(gè)節(jié)點(diǎn)。

  • 持久順序節(jié)點(diǎn)(PERSISTENT_SEQUENTIAL)

他的基本特性和持久節(jié)點(diǎn)是一致的,額外的特性表現(xiàn)在順序性上。在ZooKeeper中,每個(gè)父節(jié)點(diǎn)都會(huì)為他的第一級(jí)子節(jié)點(diǎn)維護(hù)一份順序,用于記錄下每個(gè)子節(jié)點(diǎn)創(chuàng)建的先后順序?;谶@個(gè)順序特性,在創(chuàng)建子節(jié)點(diǎn)的時(shí)候,可以設(shè)置這個(gè)標(biāo)記,那么在創(chuàng)建節(jié)點(diǎn)過程中,ZooKeeper會(huì)自動(dòng)為給定節(jié)點(diǎn)加上一個(gè)數(shù)字后綴,作為一個(gè)新的、完整的節(jié)點(diǎn)名。另外需要注意的是,這個(gè)數(shù)字后綴的上限是整型的最大值。

  • 臨時(shí)節(jié)點(diǎn)(EPHEMERAL)

臨時(shí)節(jié)點(diǎn)的生命周期和客戶端的會(huì)話綁定在一起,也就是說,如果客戶端會(huì)話失效,那么這個(gè)節(jié)點(diǎn)就會(huì)被自動(dòng)清理掉。這里提到的客戶端會(huì)話失效,而非TCP連接斷開。

  • 臨時(shí)順序節(jié)點(diǎn)(EPHEMERAL_SEQUENTIAL)

在臨時(shí)節(jié)點(diǎn)基礎(chǔ)上,添加了順序的特性。

狀態(tài)信息

每個(gè)數(shù)據(jù)節(jié)點(diǎn)除了存儲(chǔ)了數(shù)據(jù)內(nèi)容外,還存儲(chǔ)了數(shù)據(jù)節(jié)點(diǎn)本身的一些狀態(tài)信息。

狀態(tài)屬性 說明
czxid 即Created ZXID,表示該節(jié)點(diǎn)被創(chuàng)建時(shí)的事務(wù)ID
mzxid 即Modified ZXID,表示該節(jié)點(diǎn)最后一次被更新時(shí)的事務(wù)ID
ctime 即Created Time
mtime 即Modified Time
version 數(shù)據(jù)節(jié)點(diǎn)的版本號(hào)
cversion 子節(jié)點(diǎn)的版本號(hào)
aversion 節(jié)點(diǎn)的ACL版本號(hào)
ephemeralOwner 創(chuàng)建該臨時(shí)節(jié)點(diǎn)的會(huì)話的sessionID。如果該節(jié)點(diǎn)是持久節(jié)點(diǎn),那么這個(gè)屬性值為0
dataLength 數(shù)據(jù)內(nèi)容長(zhǎng)度
numChildren 當(dāng)前節(jié)點(diǎn)的子節(jié)點(diǎn)個(gè)數(shù)
pzxid 表示該節(jié)點(diǎn)的子節(jié)點(diǎn)列表最后一次被修改時(shí)的事務(wù)ID。注意,只有子節(jié)點(diǎn)列表變更了才會(huì)變更pzxid,子節(jié)點(diǎn)內(nèi)容變更不會(huì)影響pzxid。

1.3 版本-保證分布式數(shù)據(jù)原子性操作

ZooKeeper中為數(shù)據(jù)節(jié)點(diǎn)引入了版本的概念,每個(gè)數(shù)據(jù)節(jié)點(diǎn)都具有三種類型的版本信息,對(duì)數(shù)據(jù)節(jié)點(diǎn)的任何更新操作都會(huì)引起版本號(hào)的變化。

版本類型 說明
version 當(dāng)前數(shù)據(jù)節(jié)點(diǎn)數(shù)據(jù)內(nèi)容的版本號(hào)
cversion 當(dāng)前數(shù)據(jù)節(jié)點(diǎn)子節(jié)點(diǎn)的版本號(hào)
aversion 當(dāng)前數(shù)據(jù)節(jié)點(diǎn)ACL變更版本號(hào)

在ZooKeeper中,version屬性正是用來實(shí)現(xiàn)樂觀鎖機(jī)制中的“寫入校驗(yàn)”的。

version = setDataRequest.getVersion();
int currentVersion = nodeRecord.stat.getVersion();
if(version != -1 && version != currentVersion) {
    throw new KeeperException.BadVersionException(path);
}
version = currentVersion + 1;

1.4 Watcher-數(shù)據(jù)變更的通知

在ZooKeeper中,引入了Watcher機(jī)制來實(shí)現(xiàn)這種分布式的通知功能。ZooKeeper允許客戶端向服務(wù)端注冊(cè)一個(gè)Watcher監(jiān)聽,當(dāng)服務(wù)器的一些指定事件出發(fā)了這個(gè)Watcher,那么就會(huì)向指定客戶端發(fā)送一個(gè)事件通知來實(shí)現(xiàn)分布式的通知功能。

Wacher機(jī)制概述

從圖中我們可以看到,ZooKeeper的Watcher機(jī)制主要包括ke'hu'duan'xian'c客戶端線程、客戶端WatcherManager和ZooKeeper服務(wù)器三部分。在具體工作流程上,客戶端在向ZooKeeper服務(wù)器注冊(cè)Watcher的同時(shí),會(huì)將Watcher對(duì)象存儲(chǔ)在客戶端的WatcherManager中。當(dāng)ZooKeeper服務(wù)器端觸發(fā)Watcher事件后,會(huì)向客戶端發(fā)送通知,客戶端線程從WatcherManager中取出對(duì)應(yīng)的Watcher對(duì)象來執(zhí)行回調(diào)邏輯。

1.5 ACL--保障數(shù)據(jù)的安全

提到權(quán)限控制,我們首先看看目前應(yīng)用最廣泛的權(quán)限控制方式--UGO(User、Group和Others)權(quán)限控制機(jī)制。簡(jiǎn)單地講,UGO就是針對(duì)一個(gè)文件或目錄,對(duì)創(chuàng)建者(User)、創(chuàng)建者所在的組(Group)和其他用戶(Other)分別配置不同的權(quán)限。從這里可以看出,UGO其實(shí)是一種粗粒度的文件系統(tǒng)權(quán)限控制模式,利用UGO只能對(duì)三類用戶jin'xing進(jìn)行權(quán)限控制,即文件的創(chuàng)建者、創(chuàng)建者所在的組以及其他所有用戶,很顯然,UGO無法解決下面這個(gè)場(chǎng)景:

用戶U1創(chuàng)建了文件F1,希望U1所在的用戶組G1擁有對(duì)F1讀寫和執(zhí)行的權(quán)限,另一個(gè)用戶組G2擁有讀權(quán)限,而另一個(gè)用戶U3則沒有任何權(quán)限。

下面我們來看另外一種典型的權(quán)限控制方式:ACL。ACL,即訪問控制列表,是一種相對(duì)來說比較新穎且更細(xì)粒度的權(quán)限管理方式。可以針對(duì)任何用戶和組進(jìn)行細(xì)粒度的權(quán)限控制。

ACL介紹

ZooKeeper的ACL權(quán)限控制和Unix/Linux操作系統(tǒng)中的ACL有一些區(qū)別,讀者可以從三個(gè)方面來理解ACL機(jī)制,分別是:權(quán)限模式(Scheme)、授權(quán)對(duì)象(ID)和權(quán)限(Permission),通常使用“scheme:id:permission”來標(biāo)識(shí)一個(gè)有效的ACL信息。

權(quán)限模式:Scheme

權(quán)限模式用來確定權(quán)限驗(yàn)證過程中使用的校驗(yàn)策略。在ZooKeeper中,開發(fā)人員使用最多的就是以下四種權(quán)限模式。

  • IP

IP模式通過IP地址粒度來進(jìn)行權(quán)限控制。也支持按照網(wǎng)段的方式進(jìn)行配置。

  • Digest

以類似于“username:password”形式的權(quán)限標(biāo)識(shí)來進(jìn)行權(quán)限配置,便于區(qū)分不同應(yīng)用來進(jìn)行權(quán)限控制。

  • World

數(shù)據(jù)節(jié)點(diǎn)的訪問權(quán)限對(duì)所有用戶開發(fā),即所有用戶可以在不進(jìn)行任何權(quán)限校驗(yàn)的情況下操作ZooKeeper上的數(shù)據(jù)。另外,World模式也可以看作是一種特殊的Digest模式,他只有一個(gè)權(quán)限標(biāo)識(shí),即“world:anyone”。

  • Super

超級(jí)用戶的意思。

授權(quán)對(duì)象:ID

權(quán)限賦予的用戶或一個(gè)指定實(shí)體。在不同的權(quán)限模式下,授權(quán)對(duì)象是不同的。

權(quán)限:Permission

在ZooKeeper中,所有對(duì)數(shù)據(jù)的操作權(quán)限分為以下五大類:

  • CREATE(C)
  • DELETE(D)
  • READ(R)
  • WRITE(W)
  • ADMIN(A)

權(quán)限擴(kuò)展體系

實(shí)現(xiàn)自定義權(quán)限控制器

二、序列化協(xié)議

ZooKeeper的客戶端和服務(wù)端之間會(huì)進(jìn)行一系列的網(wǎng)絡(luò)通信以實(shí)現(xiàn)數(shù)據(jù)的傳輸。對(duì)于一個(gè)網(wǎng)絡(luò)通信,首先要解決的就是對(duì)數(shù)據(jù)的序列化和反序列化處理,在ZooKeeper中,使用了Jute這一序列化組件來進(jìn)行數(shù)據(jù)的序列化和反序列化操作。同時(shí),為了實(shí)現(xiàn)一個(gè)高效的網(wǎng)絡(luò)通信程序,良好的通信協(xié)議設(shè)計(jì)也是至關(guān)重要的。

通信協(xié)議

基于TCP/IP協(xié)議,ZooKeeper實(shí)現(xiàn)了自己的通信協(xié)議來完成客戶端與服務(wù)端、服務(wù)端與服務(wù)端之間的網(wǎng)絡(luò)通信。ZooKeeper通信協(xié)議整體上的設(shè)計(jì)非常簡(jiǎn)單,對(duì)于請(qǐng)求,主要包含請(qǐng)求頭和請(qǐng)求體,對(duì)于響應(yīng),則主要包含響應(yīng)頭和相應(yīng)體。

三、客戶端

客戶端是開發(fā)人員使用ZooKeeper最主要的途徑,因此我們有必要對(duì)ZooKeeper客戶端的內(nèi)部原理進(jìn)行詳細(xì)講解。ZooKeeper的客戶端主要由以下幾個(gè)核心組件組成。

  • ZooKeeper實(shí)例:客戶端的入口。
  • ClientWatchManager:客戶端Watcher管理器。
  • HostProvider:客戶端地址列表管理器。
  • ClientCnxn:客戶端核心線程,其內(nèi)部又包含兩個(gè)線程,即SendThread和EventThread。前者是一個(gè)I/O線程,主要負(fù)責(zé)ZooKeeper客戶端和服務(wù)端之間的網(wǎng)絡(luò)I/O通信;后者是一個(gè)事件線程,主要負(fù)責(zé)對(duì)服務(wù)端事件進(jìn)行處理。

客戶端的整個(gè)初始化和啟動(dòng)過程大體可以分為以下三個(gè)步驟。

    1. 設(shè)置默認(rèn)Watcher。
    1. 設(shè)置ZooKeeper服務(wù)器地址列表。
    1. 創(chuàng)建ClientCnxn。

3.1 一次會(huì)話的創(chuàng)建過程

初始化階段

  1. 初始化ZooKeeper對(duì)象。
  2. 設(shè)置會(huì)話默認(rèn)Watcher。
  3. 構(gòu)造ZooKeeper服務(wù)器地址列表管理器:HostProvider。
  4. 創(chuàng)建并初始化客戶端網(wǎng)絡(luò)連接器:ClientCnxn。
  5. 初始化SendThread和EventThread。

會(huì)話創(chuàng)建階段

  1. 啟動(dòng)SendThread和EventThread
  2. 獲取一個(gè)服務(wù)器地址。
  3. 創(chuàng)建TCP連接。
  4. 構(gòu)造ConnectRequest請(qǐng)求。
  5. 發(fā)送請(qǐng)求。

響應(yīng)處理階段

  1. 接受服務(wù)端響應(yīng)
  2. 處理Response
  3. 連接成功
  4. 生成時(shí)間:SyncConnected-None
  5. 查詢Watcher
  6. 處理事件

四、會(huì)話

會(huì)話(Session)是ZooKeeper中最重要的概念之一,客戶端和服務(wù)端之間的任何交互操作都與會(huì)話息息相關(guān),這其中就包括臨時(shí)節(jié)點(diǎn)的生命周期、客戶端請(qǐng)求的順序執(zhí)行以及Watcher通知機(jī)制等。

4.1 會(huì)話狀態(tài)

在ZooKeeper客戶端和服務(wù)端成功完成連接創(chuàng)建后,就建立了一個(gè)會(huì)話。ZooKeeper會(huì)話在整個(gè)運(yùn)行期間的聲明周期中,會(huì)在不同的會(huì)話狀態(tài)之間進(jìn)行切換,這些狀態(tài)一般可以分為CONNECTING、CONNECTED、RECONNECTING、RECONNECTED和CLOSE等。

如果客戶端需要與服務(wù)端創(chuàng)建一個(gè)會(huì)話,那么客戶端必須提供一個(gè)使用字符串表示的服務(wù)器地址列表:“host1:port,host2:port,host3:port”。一旦客戶端開始創(chuàng)建ZooKeeper對(duì)象,那么客戶端狀態(tài)就會(huì)變成CONNECTING,同時(shí)客戶端開始從上述服務(wù)器地址列表中逐個(gè)選取IP地址來嘗試進(jìn)行網(wǎng)絡(luò)連接,直到成功連接上服務(wù)器,然后將客戶端狀態(tài)變更為CONNECTED。

通常,伴隨著網(wǎng)絡(luò)閃斷或是其他原因,客戶端和服務(wù)器之間的連接會(huì)出現(xiàn)斷開情況。一旦碰到這種情況,ZooKeeper客戶端會(huì)自動(dòng)進(jìn)行重連操作,同時(shí)客戶端的狀態(tài)再次變?yōu)镃ONNECTING,直到重新連接上ZooKeeper服務(wù)器后,客戶端狀態(tài)又會(huì)再次轉(zhuǎn)變成CONNECTED。因此,在通常情況下,在ZooKeeper運(yùn)行期間,客戶端的狀態(tài)總是介于CONNECTING和CONNECTED兩者之一。

另外,如果出現(xiàn)諸如會(huì)話超時(shí)、權(quán)限檢查失敗或是客戶端主動(dòng)退出程序等情況,那么客戶端的狀態(tài)就會(huì)直接變?yōu)镃LOSE。

4.2 會(huì)話創(chuàng)建

Session

Session是ZooKeeper中的會(huì)話實(shí)體,代表了一個(gè)客戶端會(huì)話。其包含以下4個(gè)基礎(chǔ)屬性、

  • sessionId:會(huì)話id,用來唯一標(biāo)識(shí)一個(gè)會(huì)話,每次客戶端創(chuàng)建新會(huì)話的時(shí)候,ZooKeeper都會(huì)為其分配一個(gè)全局唯一的sessionId。
  • TimeOut:會(huì)話超時(shí)時(shí)間??蛻舳嗽跇?gòu)造ZooKeeper實(shí)例的時(shí)候,會(huì)配置一個(gè)sessionTimeout參數(shù)用于指定會(huì)話的超時(shí)時(shí)間。ZooKeeper客戶端向服務(wù)器發(fā)送這個(gè)超時(shí)時(shí)間后,服務(wù)器會(huì)根據(jù)自己的超時(shí)時(shí)間限制最終確定會(huì)話的超時(shí)時(shí)間。
  • TickTime:下次會(huì)話超時(shí)時(shí)間點(diǎn)。
  • isClosing:該屬性用于標(biāo)記一個(gè)會(huì)話是否已經(jīng)被關(guān)閉。

sessionID

在SeesionTracker初始化的時(shí)候,會(huì)調(diào)用initializeNextSession方法來生成一個(gè)初始化的sessionID,之后在ZooKeeper的正常運(yùn)行過程中,會(huì)在該sessionID的基礎(chǔ)上為每個(gè)會(huì)話進(jìn)行分配,其初始化算法如下:

public static long initializeNextSeesion(long id) {
    long nextSid = 0;
    nextSid = (System.currentTimeMillis() << 24) >> 8;
    nextSid = nextSid | (id << 56);
    return nextSid;
}

上面這個(gè)方法就是ZooKeeper初始化sessionID的算法,我們一起深入的探究下。從上面的代碼片段中,可以看出sessionID的生成大體可以分為以下5個(gè)步驟。

  1. 獲取當(dāng)前的毫秒表示。
  2. 左移24位。
  3. 右移8位。
  4. 添加機(jī)器標(biāo)識(shí):SID。
  5. 將步驟3和步驟4得到的兩個(gè)64位表示的數(shù)值進(jìn)行“|”操作。

簡(jiǎn)單地講,可以將上述算法概括為:高8位確定了所在機(jī)器,后56位使用當(dāng)前時(shí)間的毫秒進(jìn)行隨機(jī)。

SessionTracker

SessionTracker是ZooKeeper服務(wù)端的會(huì)話管理器,負(fù)責(zé)會(huì)話的創(chuàng)建、管理和清理等工作??梢哉f,整個(gè)會(huì)話的生命周期都離不開SessionTracker的管理。每一個(gè)會(huì)話在SessionTracker內(nèi)部都保留了三份,具體如下。

  • sessionsById:這是一個(gè)HashMap<Long, SeesionImpl>類型的數(shù)據(jù)結(jié)構(gòu),用于根據(jù)sessionID來管理Session實(shí)體。
  • sessionsWithTimeout:這是一個(gè)ConcurrentHashMap<Long, Integer>類型的數(shù)據(jù)結(jié)構(gòu),用于根據(jù)sessionID來管理會(huì)話的超時(shí)時(shí)間。該數(shù)據(jù)結(jié)構(gòu)和ZooKeeper內(nèi)存數(shù)據(jù)庫(kù)相連通,會(huì)被定期持久化到快照文件中去。
  • sessionSets:這是一個(gè)HashMap<Long, SessionSet>類型的數(shù)據(jù)結(jié)構(gòu),用于根據(jù)下次會(huì)話超時(shí)時(shí)間來歸檔會(huì)話,便于進(jìn)行會(huì)話管理和超時(shí)檢查。

創(chuàng)建連接

服務(wù)端對(duì)于客戶端的“會(huì)話創(chuàng)建”請(qǐng)求的處理,大體可以分為四大步驟,分別是ConnectRequest請(qǐng)求、會(huì)話創(chuàng)建、處理器鏈路處理和會(huì)話響應(yīng)。

4.3 會(huì)話管理

分桶策略

ZooKeeper的會(huì)話管理主要是由SessionTracker負(fù)責(zé)的,其采用了一種特殊的會(huì)化管理方式,我們稱之為“分桶策略”。所謂分桶策略,是指將類似的會(huì)話放在同一區(qū)塊中進(jìn)行管理,以便于ZooKeeper對(duì)會(huì)話進(jìn)行不同區(qū)塊的格里處理以及同一區(qū)塊的統(tǒng)一處理。

ZooKeeper將所有的會(huì)話都分配在了不同的區(qū)塊之中,分配的原則是每個(gè)會(huì)話的“下次超時(shí)時(shí)間點(diǎn)”(ExpirationTime)。ExpirationTime是指該會(huì)話最近一次可能超時(shí)的時(shí)間點(diǎn),對(duì)于一個(gè)新創(chuàng)建的會(huì)話而言,其會(huì)話創(chuàng)建完畢后,ZooKeeper就會(huì)為其計(jì)算ExpirationTime,計(jì)算方式如下:

ExpirationTime = CurrentTime + SessionTimeout

在ZooKeeper的實(shí)際實(shí)現(xiàn)中,Zookeeper的Leader服務(wù)器在運(yùn)行期間會(huì)定時(shí)的進(jìn)行會(huì)話超時(shí)檢查,其時(shí)間間隔是ExpirationInterval,單位是毫秒,默認(rèn)值是tickTime的值,即默認(rèn)情況下,每隔2000毫秒進(jìn)行一次會(huì)話超時(shí)檢查。為了方便對(duì)多個(gè)會(huì)話同時(shí)進(jìn)行超時(shí)檢查,完整的ExpirationTime的計(jì)算方式如下:

ExpirationTime_ = CurrentTime + SessionTimeout
ExpirationTime = (ExpirationTime_/ExpirationInterval + 1) * ExpirationInterval

會(huì)話激活

為了保持客戶端會(huì)話的有效性,在ZooKeeper的運(yùn)行過程中,客戶端會(huì)在會(huì)話超時(shí)時(shí)間國(guó)企范圍內(nèi)向服務(wù)端發(fā)送PING請(qǐng)求來保持會(huì)話的有效性,我們俗稱“心跳檢測(cè)”。同時(shí),服務(wù)端需要不斷地接收來自客戶端的這個(gè)心跳檢測(cè),并且需要重新激活對(duì)應(yīng)的客戶端會(huì)話,我們將這個(gè)重新激活的過程稱為TouchSession。會(huì)話激活的過程,不僅能夠使服務(wù)端檢測(cè)到對(duì)應(yīng)客戶端的存活性,也能讓客戶端自己保持連接狀態(tài)。

會(huì)話超時(shí)檢查

在ZooKeeper中,會(huì)話超時(shí)檢查同樣是由SessionTracker負(fù)責(zé)的。SessionTracker中有一個(gè)單獨(dú)的線程專門進(jìn)行會(huì)話超時(shí)檢查,這里我們稱其為“超時(shí)檢查線程”,其工作機(jī)制的核心思路非常簡(jiǎn)單:逐個(gè)依次對(duì)會(huì)話桶中剩下的會(huì)話進(jìn)行清理。

4.4 會(huì)話清理

當(dāng)SessionTracker的會(huì)話超時(shí)檢查線程整理出一些已經(jīng)過期的會(huì)話后,那么就要開始進(jìn)行會(huì)話清理了。會(huì)話清理的步驟大致可以分為以下七步。

  1. 標(biāo)記會(huì)話狀態(tài)為“已關(guān)閉”

為了保證在清理期間不再處理來自該客戶端的新請(qǐng)求,SessionTracker會(huì)首先將該會(huì)話的isClosing屬性標(biāo)記為true。

  1. 發(fā)起“會(huì)話關(guān)閉”請(qǐng)求

為了使該會(huì)話的關(guān)閉操作在整個(gè)服務(wù)端集群中都生效,ZooKeeper使用了提交“會(huì)話關(guān)閉”請(qǐng)求的方式,并立即交付給PrepRequestProcessor處理器進(jìn)行處理。

  1. 收集需要清理的臨時(shí)節(jié)點(diǎn)

在ZooKeeper的內(nèi)存數(shù)據(jù)庫(kù)中,為每個(gè)會(huì)話都單獨(dú)保存了一份由該會(huì)話維護(hù)的所有臨時(shí)節(jié)點(diǎn)集合,因此在會(huì)話清理階段,只需要根據(jù)當(dāng)前即將關(guān)閉的會(huì)話的sessionID從內(nèi)存數(shù)據(jù)庫(kù)中獲取到這份臨時(shí)節(jié)點(diǎn)列表即可。

實(shí)際上,有如下細(xì)節(jié)需要處理:在ZooKeeper處理會(huì)話關(guān)閉請(qǐng)求之前,正好有以下請(qǐng)求到達(dá)了服務(wù)端并正在處理中:

  • 節(jié)點(diǎn)刪除請(qǐng)求,刪除的目標(biāo)節(jié)點(diǎn)正好是上述臨時(shí)節(jié)點(diǎn)中的一個(gè)。
  • 臨時(shí)節(jié)點(diǎn)創(chuàng)建請(qǐng)求,創(chuàng)建的目標(biāo)節(jié)點(diǎn)正好是上述臨時(shí)節(jié)點(diǎn)中的一個(gè)。

嘉定我們當(dāng)前獲取的臨時(shí)節(jié)點(diǎn)列表是ephemerals,那么針對(duì)第一類請(qǐng)求,我們需要將所有這些請(qǐng)求對(duì)應(yīng)的數(shù)據(jù)節(jié)點(diǎn)路徑從ephemerals中移除,以避免重復(fù)刪除。針對(duì)第二類,我們需要將所有這些請(qǐng)求對(duì)應(yīng)的數(shù)據(jù)節(jié)點(diǎn)路徑添加到ephemerals中去,以刪除這些即將會(huì)被創(chuàng)建但是尚未保存到內(nèi)存數(shù)據(jù)庫(kù)中去的臨時(shí)節(jié)點(diǎn)。

  1. 添加“節(jié)點(diǎn)刪除”事務(wù)變更

完成該會(huì)話相關(guān)的臨時(shí)節(jié)點(diǎn)收集后,ZooKeeper會(huì)逐個(gè)將這些臨時(shí)節(jié)點(diǎn)轉(zhuǎn)換成“節(jié)點(diǎn)刪除”請(qǐng)求,并放入事務(wù)變更隊(duì)列outstandingChanges中去。

  1. 刪除臨時(shí)節(jié)點(diǎn)

FinalRequestProcessor處理器會(huì)觸發(fā)內(nèi)存數(shù)據(jù)庫(kù),刪除該會(huì)話對(duì)應(yīng)的所有臨時(shí)節(jié)點(diǎn)。

  1. 移除會(huì)話

完成節(jié)點(diǎn)刪除后,需要將會(huì)話從SessionTracker中移除。主要就是從上面提到的三個(gè)數(shù)據(jù)結(jié)構(gòu)(sessionById、sessionsWithTimeout和sessionSets)中將該會(huì)話移除掉。

  1. 關(guān)閉NIOServerCnxn

最后,從NIOServerCnxnFactory找到該會(huì)話對(duì)應(yīng)的NIOServerCnxn,將其關(guān)閉。

4.5 重連

當(dāng)客戶端和服務(wù)端之間的網(wǎng)絡(luò)連接斷開時(shí),ZooKeeper客戶端會(huì)自動(dòng)進(jìn)行反復(fù)的重連,知道最終成功連接上ZooKeeper集群中的一臺(tái)機(jī)器。在這種情況下,再次連接上服務(wù)端的客戶端有可能會(huì)處于以下兩種狀態(tài)之一。

  • CONNECTED:重連成功
  • EXPIRED:如果是在會(huì)話超時(shí)時(shí)間以外重新連接上,那么服務(wù)端其實(shí)已經(jīng)對(duì)該會(huì)話進(jìn)行了會(huì)話清理操作,因此再次連接上的會(huì)話將被視為非法會(huì)話。

當(dāng)客戶端和服務(wù)端之間的連接斷開后,用戶在客戶端可能會(huì)看到兩類異常:CONNECTION_LOSS(連接斷開)和SESSION_EXPIRED(會(huì)話過期)。

五、服務(wù)器啟動(dòng)

我們首先看看ZooKeeper服務(wù)端的整體架構(gòu),如圖

ZooKeeper服務(wù)端整體架構(gòu)

5.1 單機(jī)版服務(wù)器啟動(dòng)

ZooKeeper服務(wù)器的啟動(dòng),大體可以分為以下五個(gè)主要步驟:配置文件解析、初始化數(shù)據(jù)管理器、初始化網(wǎng)絡(luò)I/O管理器、數(shù)據(jù)恢復(fù)和對(duì)外服務(wù)。下圖是單機(jī)版ZooKeeper服務(wù)器的啟動(dòng)流程圖。

單機(jī)版ZooKeeper服務(wù)器的啟動(dòng)流程

預(yù)啟動(dòng)

預(yù)啟動(dòng)的步驟如下。

  1. 統(tǒng)一由QuorumPeerMain作為啟動(dòng)類
  2. 解析配置文件zoo.cfg
  3. 創(chuàng)建并啟動(dòng)歷史文件清理器DatadirCleanupManager
  4. 判斷當(dāng)前是集群模式還是單機(jī)模式的啟動(dòng)
  5. 再次進(jìn)行配置文件zoo.cfg的解析
  6. 創(chuàng)建服務(wù)器實(shí)例ZooKeeperServer

初始化

初始化的步驟如下。

  1. 創(chuàng)建服務(wù)器統(tǒng)計(jì)器ServerStats
  2. 創(chuàng)建ZooKeeper數(shù)據(jù)管理器FileTxnSnapLog
  3. 設(shè)置服務(wù)器tickTime和會(huì)話超時(shí)時(shí)間限制
  4. 創(chuàng)建ServerCnxnFactory
  5. 初始化ServerCnxnFactory
  6. 啟動(dòng)ServerCnxnFactory主線程
  7. 恢復(fù)本地?cái)?shù)據(jù)
  8. 創(chuàng)建并啟動(dòng)會(huì)話管理器
  9. 初始化ZooKeeper的請(qǐng)求處理鏈
  10. 注冊(cè)JMX服務(wù)
  11. 注冊(cè)ZooKeeper服務(wù)器實(shí)例

5.2 集群版服務(wù)器啟動(dòng)

集群版和單機(jī)版ZooKeeper服務(wù)器啟動(dòng)過程在很多地方是一致的,所以這里只會(huì)對(duì)有差異的地方展開進(jìn)行講解。下圖是集群版ZooKeeper服務(wù)器的啟動(dòng)流程圖。

集群版ZooKeeper服務(wù)器的啟動(dòng)流程

預(yù)啟動(dòng)

預(yù)啟動(dòng)的步驟如下。

  1. 統(tǒng)一由QuorumPeerMain作為啟動(dòng)類
  2. 解析配置文件zoo.cfg
  3. 創(chuàng)建并啟動(dòng)歷史文件清理器DatadirCleanupManager
  4. 判斷當(dāng)前是集群模式還是單機(jī)模式啟動(dòng)

初始化

初始化的步驟如下

  1. 創(chuàng)建ServerCnxnFactory
  2. 初始化ServerCnxnFactory
  3. 創(chuàng)建ZooKeeper數(shù)據(jù)管理器FileTxnSnapLog
  4. 創(chuàng)建QuorumPeer
  5. 創(chuàng)建內(nèi)存數(shù)據(jù)庫(kù)ZKDatabase
  6. 初始化QuorumPeer
  7. 恢復(fù)本地?cái)?shù)據(jù)
  8. 啟動(dòng)ServerCnxnFactory主線程

Leader選舉

Leader選舉的步驟如下

  1. 初始化Leader選舉
  2. 注冊(cè)JMX服務(wù)
  3. 監(jiān)測(cè)當(dāng)前服務(wù)器狀態(tài)
  4. Leader選舉

Leader和Follower啟動(dòng)期交互過程

Leader和Follower啟動(dòng)期交互過程

六、Leader選舉

6.1 Leader選舉概述

服務(wù)器啟動(dòng)時(shí)期的Leader選舉

要進(jìn)行Leader選舉的時(shí)候,隱式條件便是ZooKeeper的集群規(guī)模至少是2臺(tái)機(jī)器,只有一臺(tái)服務(wù)器啟動(dòng)的時(shí)候,是無法進(jìn)行Leader選舉的。

  1. 每個(gè)Server會(huì)發(fā)出一個(gè)投票

初始情況,對(duì)于Server1和Server2來說,都會(huì)投給自己,每次投票包含的最基本的元素包括:所推舉的服務(wù)器myid和ZXID。

  1. 接收來自各個(gè)服務(wù)器的投票

集群中每個(gè)服務(wù)器在收到投票后,首先會(huì)判斷投票的有效性,包含檢查是否是本輪投票,是否來自LOOKING狀態(tài)的服務(wù)器。

  1. 處理投票

在接收到來自其他服務(wù)器的投票后,針對(duì)每個(gè)投票,服務(wù)器都需要將別人的投票和自己的投票進(jìn)行PK,PK的規(guī)則如下

  • 優(yōu)先檢查ZXID。ZXID比較大d服務(wù)器優(yōu)先作為L(zhǎng)eader
  • 如果ZXID相同的話,那么就比較myid。myid比較大的服務(wù)器作為L(zhǎng)eader服務(wù)器。
  1. 統(tǒng)計(jì)投票

每次投票后,服務(wù)器都會(huì)統(tǒng)計(jì)所有投票,判斷是否已經(jīng)有過半機(jī)器接收到相同的投票信息。

  1. 改變服務(wù)器狀態(tài)

一旦確定了Leader,每個(gè)服務(wù)器就會(huì)更新自己的狀態(tài):如果是Follower,那么就變更為FOLLOWING,如果是Leader,那么就變更為L(zhǎng)EADING。

服務(wù)器運(yùn)行期間的Leader選舉

在ZooKeeper集群正常運(yùn)行過程中,一旦選出一個(gè)Leader,那么所有服務(wù)器的集群角色一般不會(huì)再發(fā)生變化,不管是是非Leader集群掛了還是新機(jī)器加入集群,都不會(huì)影響Leader。一旦Leader掛了,那么整個(gè)集群將暫時(shí)無法對(duì)外服務(wù),而是進(jìn)入新一輪的Leader選舉。

6.2 Leader選舉的算法分析

在ZooKeeper中,提供了三種Leader選舉的算法,分別是LeaderElection、UDP版本的FastLeaderElection和TCP版本的FastLeaderElection,可以通過在配置文件zoo.cfg中使用electionAlg屬性來指定,分別用數(shù)字0-3表示。0表示LeaderElection,1表示UDP版本的FastLeaderElection,并且是非授權(quán)模式,2表示UDP版本的FastLeaderElection,使用授權(quán)模式,3代表TCP版本的FastLeaderElection。從3.4.0版本開始,Zookeeper廢棄了0-2這三種算法,只保留了TCP版本的FastLeaderElection選舉算法。

術(shù)語(yǔ)解釋

  • SID:服務(wù)器ID
  • ZXID:事務(wù)ID
  • Vote:投票
  • Quorum:過半機(jī)器數(shù)

算法分析

進(jìn)入Leader選舉

當(dāng)ZooKeeper集群中的一臺(tái)服務(wù)器出現(xiàn)以下兩種情況時(shí),就會(huì)開始進(jìn)入Leader選舉

  • 服務(wù)器初始化啟動(dòng)
  • 服務(wù)器運(yùn)行期間無法和Leader保持連接

而當(dāng)一臺(tái)機(jī)器進(jìn)入Leadeader選舉流程時(shí),當(dāng)前集群也可能會(huì)處于以下兩種狀態(tài)

  • 集群中本來就存在一個(gè)Leader
  • 集群中確實(shí)不存在Leader

第一種情況,這種情況通常是某一臺(tái)服務(wù)器啟動(dòng)比較晚,在他啟動(dòng)之前,集群已經(jīng)可以正常工作。針對(duì)這種情況,當(dāng)該機(jī)器試圖去選舉Leader時(shí),會(huì)被告知當(dāng)前服務(wù)器的Leader信息,對(duì)于該機(jī)器來說,僅僅需要和Leader機(jī)器建立起連接,并進(jìn)行狀態(tài)同步即可。

下面我們看看集群中不存在Leader的情況下,如何進(jìn)行Leader選舉。

開始第一次投票

通常有兩種情況會(huì)導(dǎo)致集群中不存在Leader,一種是整個(gè)服務(wù)器剛剛初始化啟動(dòng)時(shí),另一種情況就是運(yùn)行期間當(dāng)前Leader所在的服務(wù)器掛了。此時(shí),集群中所有機(jī)器都處于LOOKING的狀態(tài)。當(dāng)一臺(tái)服務(wù)器處于LOOKING狀態(tài)時(shí),那么他就會(huì)向集群中所有其他機(jī)器發(fā)送消息,我們稱這個(gè)消息為“投票”。

在這個(gè)投票消息中包含了兩個(gè)最基本的信息:所推舉的服務(wù)器SID和ZXID,用(SID,ZXID)表示。一般都是投自己。

變更投票

集群中每臺(tái)機(jī)器發(fā)出自己的投票后,也會(huì)接收到來自集群中其他機(jī)器的投票。每臺(tái)機(jī)器都會(huì)根據(jù)一定的規(guī)則,來處理收到的其他機(jī)器的投票,并以此來決定是否需要變更自己的投票。這個(gè)規(guī)則也成了整個(gè)Leader選舉算法的核心所在。我們首先定義一些術(shù)語(yǔ)。

  • vote_sid:接收到的投票中所推舉Leader服務(wù)器的SID
  • vote_zxid:接收到的投票中所推舉Leader服務(wù)器的ZXID
  • self_sid:當(dāng)前服務(wù)器自己的SID
  • self_zxid:當(dāng)前服務(wù)器自己的ZXID

對(duì)比過程如下:

  • 規(guī)則1:如果vote_zxid>self_zxid,就認(rèn)可當(dāng)前收到投票,并再次將該投票發(fā)送出去。
  • 規(guī)則2:如果vote_zxid<self_zxid,就堅(jiān)持自己的投票,不作任何變更。
  • 規(guī)則3:如果vote_zxid=self_zxid,就對(duì)比兩者的SID。如果vote_sid>self_sid,就認(rèn)可當(dāng)前收到的投票,并在此將該投票發(fā)出去。
  • 規(guī)則4:如果vote_zxid=self_zxid,并且vote_sid<self_sid,那么同樣堅(jiān)持自己的投票,不作變更。

確定Leader

經(jīng)過這第二次投票后,集群中每臺(tái)機(jī)器都會(huì)再次受到其他機(jī)器的投票,然后開始統(tǒng)計(jì)投票。如果一臺(tái)機(jī)器收到了超過半數(shù)的相同的投票,那么這個(gè)投票對(duì)應(yīng)的SID機(jī)器ji'wei即為L(zhǎng)eader。

小結(jié)

通常哪臺(tái)服務(wù)器上的越新,那么越有可能成為L(zhǎng)eader,原因很簡(jiǎn)單,數(shù)據(jù)越新,ZXID越大,也就越能夠保證數(shù)據(jù)的恢復(fù)。

6.3 Leader選舉的實(shí)現(xiàn)細(xì)節(jié)

服務(wù)器狀態(tài)

  • LOOKING
  • FOLLOWING
  • LEADING
  • OBSERVING

投票數(shù)據(jù)結(jié)構(gòu)

屬性 說明
id 被推舉的Leader的SID值
zxid 被推舉的Leader的事務(wù)ID
electionEpoch 邏輯時(shí)鐘,用來判斷多個(gè)投票是否在同一輪選舉周期中。該值在服務(wù)端是一個(gè)自增序列。每次進(jìn)入新一輪投票后,都會(huì)對(duì)該值進(jìn)行加一
peerEpoch 被推舉的Leader的epoch
state 當(dāng)前服務(wù)器狀態(tài)

QuorumCnxManager:網(wǎng)絡(luò)I/O

每臺(tái)服務(wù)器啟動(dòng)的時(shí)候,都會(huì)啟動(dòng)一個(gè)QuorumCnxManager,負(fù)責(zé)各臺(tái)服務(wù)器之間的底層Leader選舉過程中的網(wǎng)絡(luò)通信。

消息隊(duì)列

在QuorumCnxManager這個(gè)類內(nèi)部維護(hù)了一系列的隊(duì)列,用于保存接收到的、待發(fā)送的消息,以及消息的發(fā)送器。

  • recvQueue:消息接收隊(duì)列
  • queueSendMap:消息發(fā)送隊(duì)列,用于保存那些待發(fā)送的消息
  • senderWorkerMap:發(fā)送器集合
  • lastMessageSent:最近發(fā)送過的消息

建立連接

QuorumCnxManager在啟動(dòng)的時(shí)候,會(huì)創(chuàng)建一個(gè)ServerSocket來監(jiān)聽Leader選舉的通信接口(Leader選舉的通信端口默認(rèn)是3888)。開啟端口監(jiān)聽后,ZooKeeper就能夠不斷地接收到來自其他服務(wù)器的“創(chuàng)建連接”請(qǐng)求,在收到其他服務(wù)器的TCP連接請(qǐng)求時(shí),會(huì)交由receiveConnection函數(shù)來處理。為了避免兩臺(tái)機(jī)器之間重復(fù)的創(chuàng)建TCP連接,ZooKeeper設(shè)計(jì)了建立TCP連接的規(guī)則:只允許SID大的服務(wù)器主動(dòng)與其他服務(wù)器建立連接,否則斷開鏈接。

一旦建立起連接,就會(huì)根據(jù)遠(yuǎn)程服務(wù)器的SID來創(chuàng)建相應(yīng)的消息發(fā)送器SendWorker和消息接收器RecvWorker,并啟動(dòng)他們。

消息接收與發(fā)送

消息的接收過程是由消息接收器RecvWorker來負(fù)責(zé)的。ZooKeeper會(huì)為每個(gè)遠(yuǎn)程服務(wù)器分配一個(gè)單獨(dú)的RecvWorker,每個(gè)RecvWorker只需要不斷地從這個(gè)TCP連接中讀取消息,并將其保存到recvQueue隊(duì)列中。

消息發(fā)送過程也比較簡(jiǎn)單,由于ZooKeeper同樣已經(jīng)為每個(gè)遠(yuǎn)程服務(wù)器單獨(dú)分別分配了消息發(fā)送器SendWorker,那么每個(gè)SendWorker只需要不斷地從對(duì)應(yīng)的消息發(fā)送隊(duì)列中取出一個(gè)消息來發(fā)送即可,同時(shí)將這個(gè)消息放入lastMessageSent中來作為最近發(fā)送過的消息。

FastLeaderElection:選舉算法的核心部分

先約定幾個(gè)概念:

  • 外部投票:其他服務(wù)器發(fā)來的投票
  • 內(nèi)部投票:自身當(dāng)前的投票
  • 選舉輪次:ZooKeeper服務(wù)器Leader選舉的輪次,即logicalclock
  • PK:指對(duì)內(nèi)部投票和外部投票進(jìn)行一個(gè)對(duì)比來確定是否需要變更內(nèi)部投票

選票管理

  • sendqueue:選票發(fā)送隊(duì)列,用于保存待發(fā)送的選票
  • recvqueue:選票接收隊(duì)列,用于保存接收到的外部投票
  • WorkerReceiver:選票接收器。該接收器會(huì)不斷地從QuorumCnxManager中獲取其他服務(wù)器發(fā)來的選舉消息,并將其轉(zhuǎn)換成一個(gè)選票,然后保存到recvQueue隊(duì)列中去。在選票接收過程中,如果發(fā)現(xiàn)該外部投票的選舉輪次小于當(dāng)前服務(wù)器,就直接忽略這個(gè)外部投票,同時(shí)立即發(fā)出自己的內(nèi)部投票。當(dāng)然,如果當(dāng)前服務(wù)器并不是LOOKING狀態(tài),即yi'j已經(jīng)選出了Leader,那么也將忽略這個(gè)外部投票,同時(shí)將Leader信息已投票信息發(fā)送出去。另外,如果接收到的消息來自O(shè)bserver服務(wù)器,那么就直接忽略掉,并將自己當(dāng)前的投票發(fā)送出去。
  • WorkerSender:選票發(fā)送器,會(huì)不斷從sendqueue隊(duì)列中獲取待發(fā)送的選票,并將其傳遞到底層QuorumCnxManager中去。

算法核心

Leader選舉算法實(shí)現(xiàn)的流程示意圖

七、各服務(wù)器角色介紹

7.1 Leader

Leader服務(wù)器是整個(gè)ZooKeeper集群工作機(jī)制的核心,其主要工作有以下兩個(gè)。

  • 事務(wù)請(qǐng)求的唯一調(diào)度和處理者,保證集群事務(wù)處理的順序性。
  • 集群內(nèi)部各服務(wù)器的調(diào)度者。

7.2 Follower

Follower服務(wù)器是ZooKeeper集群狀態(tài)的跟隨者,主要工作

  • 處理客戶端非事務(wù)請(qǐng)求,轉(zhuǎn)發(fā)事務(wù)請(qǐng)求給Leader服務(wù)器。
  • 參與事務(wù)請(qǐng)求Proposal的投票
  • 參與Leader選舉投票

7.3 Observer

工作原理與Follower基本一致,唯一區(qū)別在于Observer不參與任何形式的投票,包括事務(wù)請(qǐng)求Proposal的投票和Leader選舉投票。簡(jiǎn)單的講,Observer服務(wù)器只提供非事務(wù)服務(wù),通常用于在不影響集群事務(wù)處理能力的前提下提升集群的非事務(wù)處理能力。

7.4 集群間消息通信

ZooKeeper的消息類型大體上可以分為四類,分別是:數(shù)據(jù)同步型、服務(wù)器初始化型、請(qǐng)求處理型和會(huì)話管理型。

數(shù)據(jù)同步型

是指在Learner和Leader服務(wù)器進(jìn)行數(shù)據(jù)同步的時(shí)候,網(wǎng)絡(luò)通信所用到的消息,通常有DIFF、TRUNC、SNAP和UPTPDATE四種。

服務(wù)器初始化型

是指在整個(gè)集群或是某些新機(jī)器初始化時(shí),Leader和Learner之間相互通信所使用的消息類型,常見的有OBSEERVERINFO、FOLLOWERINFO、LEADERINFO、ACKEPOCH和NEWLEADER五種。

請(qǐng)求處理型

是指在進(jìn)行請(qǐng)求處理的過程中,Leader和Learner服務(wù)器之間相互通信所使用的消息,常見的有REQUEST、PROPOSAL、ACK、COMMIT、INFORM和SYNC六種。

會(huì)話管理型

是指ZooKeeper在進(jìn)行會(huì)話管理的過程中,和Learner服務(wù)器之間互相通信所使用的消息,常見的有PING和REVALIDATE兩種。

八、請(qǐng)求處理

8.1 會(huì)話創(chuàng)建請(qǐng)求

ZooKeeper服務(wù)端對(duì)于會(huì)話創(chuàng)建的處理,大體可以分為請(qǐng)求接收、會(huì)話創(chuàng)建、預(yù)處理、事務(wù)處理、事務(wù)應(yīng)用和會(huì)話響應(yīng)6大環(huán)節(jié)。

8.2 SetData請(qǐng)求

服務(wù)端對(duì)于SetData請(qǐng)求的處理,大體可以分為4大步驟,分別是請(qǐng)求的預(yù)處理、事務(wù)處理、事務(wù)應(yīng)用和請(qǐng)求響應(yīng)。

8.3 事務(wù)請(qǐng)求轉(zhuǎn)發(fā)

在事務(wù)請(qǐng)求的處理過程中,需要我們注意的一個(gè)細(xì)節(jié)是,為了保證事務(wù)請(qǐng)求被順序執(zhí)行,從而確保ZooKeeper集群的數(shù)據(jù)一致性,所有的事務(wù)請(qǐng)求必須由Leader服務(wù)器來處理。但是,并不是所有的ZooKeeper都和Leader服務(wù)器保持連接,那么如何保證所有的事務(wù)請(qǐng)求都由Leader來處理呢?

ZooKeeper實(shí)現(xiàn)了非常特別的事務(wù)請(qǐng)求轉(zhuǎn)發(fā)機(jī)制:所有非Leader服務(wù)器如果接收到了來自客戶端的事務(wù)請(qǐng)求,那么必須將其轉(zhuǎn)發(fā)給Leader服務(wù)器來處理。

8.4 GetData請(qǐng)求

服務(wù)端對(duì)于GetData請(qǐng)求的處理,大體可以分為3大步驟,分別是請(qǐng)求的預(yù)處理、非事務(wù)處理和請(qǐng)求響應(yīng)。

九、小結(jié)

ZooKeeper以樹作為其內(nèi)存數(shù)據(jù)模型,樹上的每一個(gè)節(jié)點(diǎn)是最小的數(shù)據(jù)單元,即ZNode。ZNode具有不同的節(jié)點(diǎn)特性,同時(shí)每個(gè)節(jié)點(diǎn)都具有一個(gè)遞增的版本號(hào),以此可以實(shí)現(xiàn)分布式數(shù)據(jù)的原子性更新。

ZooKeeper的序列化層使用從Hadoop中遺留下來的Jute組件,該組件并不是性能最好的序列化框架,但是在ZooKeeper中已經(jīng)夠用。

ZooKeeper的客戶端和服務(wù)端之間會(huì)建立起TCP長(zhǎng)連接來進(jìn)行網(wǎng)絡(luò)通信,基于該TCP連接衍生出來的會(huì)話概念,是客戶端和服務(wù)端之間所有請(qǐng)求和響應(yīng)交互的基石。在會(huì)話的生命周期中,會(huì)出現(xiàn)連接斷開、重連或是會(huì)話失效等一系列問題,這些都是ZooKeeper的會(huì)話管理器需要處理的問題--Leader服務(wù)器會(huì)負(fù)責(zé)管理每個(gè)會(huì)話的生命周期,包括會(huì)話的創(chuàng)建、心跳檢測(cè)和銷毀等。

在服務(wù)器啟動(dòng)階段,會(huì)進(jìn)行磁盤數(shù)據(jù)的恢復(fù),完成數(shù)據(jù)恢復(fù)后就會(huì)進(jìn)行Leader選舉。一旦選舉產(chǎn)生Leader服務(wù)器后,就立即開始進(jìn)行集群間的數(shù)據(jù)同步--在整個(gè)過程中,ZooKeeper都處于不可用狀態(tài),知道數(shù)據(jù)同步完畢(集群中絕大部分機(jī)器數(shù)據(jù)和Leader一致),ZooKeeper才可以對(duì)外提供正常服務(wù)。在運(yùn)行期間,如果Leader服務(wù)器所在的機(jī)器掛掉或是和集群中絕大部分服務(wù)器斷開連接,那么就會(huì)觸發(fā)新一輪的Leader選舉。同樣,在新的Leader服務(wù)器選舉產(chǎn)生之前,ZooKeeper無法對(duì)外提供服務(wù)。

一個(gè)正常運(yùn)行的ZooKeeper集群,其機(jī)器通常由Leader、Follower和Observer組成。ZooKeeper對(duì)于客戶端請(qǐng)求的處理,嚴(yán)格按照Z(yǔ)AB協(xié)議規(guī)范來進(jìn)行。每個(gè)服務(wù)器在啟動(dòng)初始化階段都會(huì)組裝一個(gè)請(qǐng)求處理鏈,Leader服務(wù)器能夠處理所有類型的客戶端請(qǐng)求,而對(duì)于Follower或是Observer服務(wù)器來說,可以正常處理非事務(wù)請(qǐng)求,而事務(wù)請(qǐng)求則需要轉(zhuǎn)發(fā)給Leader服務(wù)器來處理,同時(shí),對(duì)于每個(gè)事務(wù)請(qǐng)求,Leader都會(huì)為其分配一個(gè)全局唯一且遞增的ZXID,以此來保證事務(wù)處理的順序性。在事務(wù)請(qǐng)求的處理過程中,Leader和Follower服務(wù)器都會(huì)進(jìn)行事務(wù)日志的記錄。

ZooKeeper通過JDK的File接口簡(jiǎn)單實(shí)現(xiàn)了自己的數(shù)據(jù)存儲(chǔ)系統(tǒng),其底層數(shù)據(jù)存儲(chǔ)包括事務(wù)日志和快照數(shù)據(jù)兩部分,這些都是ZooKeeper實(shí)現(xiàn)數(shù)據(jù)一致性非常關(guān)鍵的部分。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容