背景
我們都知道 Zookeeper 是基于 ZAB 協(xié)議實(shí)現(xiàn)的,在介紹 ZAB 協(xié)議之前,先回顧一下 Zookeeper 的起源與發(fā)展。
Zookeeper 究竟是在什么樣的時(shí)代背景下被提出?為了解決了哪些棘手的問題?
Zookeeper 最早起源于雅虎研究院的一個(gè)研究小組。當(dāng)時(shí),研究人員發(fā)現(xiàn),在雅虎的很多大型系統(tǒng)基本都需要依賴一個(gè)類似的系統(tǒng)來進(jìn)行分布式協(xié)調(diào),但是這些系統(tǒng)都存在分布式單點(diǎn)問題,所以雅虎的開發(fā)人員就試圖開發(fā)出一個(gè)通用的無單點(diǎn)問題的分布式協(xié)調(diào)框架,以便讓開發(fā)人員將精力集中在處理業(yè)務(wù)邏輯上。
于是,Zookeeper 就誕生了!
Zookeeper 的出現(xiàn)不僅解決了分布式系統(tǒng)下數(shù)據(jù)一致性的問題,而且經(jīng)歷過線上驗(yàn)證,無論是從性能、易用性、穩(wěn)定性上來說,都是工業(yè)級(jí)產(chǎn)品的標(biāo)準(zhǔn)。可以說在分布式系統(tǒng)中具有不可替代的核心地位,Hadoop、HBase、Storm 和 Solr 等大型分布式系統(tǒng)都已經(jīng)將 Zookeeper 作為其核心組件,用于分布式協(xié)調(diào)。即便 Zookeeper 如此優(yōu)秀,但是 Zookeeper 依然是免費(fèi)且開源的,全世界成千上萬的開發(fā)者都關(guān)注著它,陪同著一起成長(zhǎng)和發(fā)展。
作為一個(gè)開發(fā)者無論是為了應(yīng)付面試、晉升還是個(gè)人技術(shù)成長(zhǎng)的需要,都需要對(duì) Zookeeper 有所了解,而學(xué)習(xí) Zookeeper 的關(guān)鍵就是理解其核心部分:
ZAB 協(xié)議, 全稱(Zookeeper Atomic Broadcast)Zookeeper 原子消息廣播協(xié)議。
它與 Paxos 類似,也是一種數(shù)據(jù)一致性的算法。
Zookeeper應(yīng)該具備的特性
在 ZAB 協(xié)議的開發(fā)設(shè)計(jì)人員在協(xié)議設(shè)計(jì)之初并沒有要求 ZAB 具有很好的擴(kuò)展性,最初只是為了雅虎公司內(nèi)部哪些高吞吐量、低延遲、健壯、簡(jiǎn)單的分布式系統(tǒng)場(chǎng)景設(shè)計(jì)的。基于 ZAB 協(xié)議,Zookeeper 實(shí)現(xiàn)了一種主備模式的系統(tǒng)架構(gòu)來保持集群中各副本之間數(shù)據(jù)的一致性,簡(jiǎn)單架構(gòu)圖如下:
Zookeeper 用一個(gè)單一的主進(jìn)程來接收并處理客戶端的所有事物請(qǐng)求,并采用 ZAB 的原子廣播協(xié)議將服務(wù)器數(shù)據(jù)狀態(tài)以事物 Proposal 的形式廣播到所有的副本進(jìn)程中去。這樣的模式就保證了,在同一時(shí)刻只有一個(gè)主進(jìn)程來廣播服務(wù)器的狀態(tài)更變,因此能夠很好地處理客戶端大量的并發(fā)請(qǐng)求,這在 ZAB 協(xié)議中叫:消息廣播。
除此之外,在分布式環(huán)境中事物的執(zhí)行順序也會(huì)存在一定的先后關(guān)系,比如:事務(wù) C 的寫入需要依賴事務(wù) B 的寫入,而事務(wù) B 寫入需要依賴事務(wù) A 寫入。這種前后依賴的順序也對(duì) ZAB 協(xié)議提出了一個(gè)要求:ZAB 協(xié)議需要保證如果一個(gè)狀態(tài)的變更被處理了,那么所有其依賴的狀態(tài)變更都已經(jīng)被提前處理了。也就是需要順序執(zhí)行。
另外除了能正常廣播消息、消息的順序執(zhí)行,主進(jìn)程也可能隨時(shí)會(huì)因?yàn)閿嚯姟C(jī)器宕機(jī)等異常情況無法提供服務(wù),因此,ZAB 協(xié)議還需要做到在當(dāng)前主進(jìn)程出現(xiàn)上述異常情況的時(shí)候依然能夠正常工作,這在 ZAB 協(xié)議中叫:崩潰恢復(fù)。
所以整個(gè) ZAB 協(xié)議需要具備的核心特性已經(jīng)被描述出來了,處理事務(wù)的請(qǐng)求的方式可以簡(jiǎn)單理解為:
所有的事務(wù)請(qǐng)求必須由一個(gè)全局唯一的服務(wù)器來協(xié)調(diào)處理,這樣的服務(wù)器叫:Leader 服務(wù)器。其他的服務(wù)器被稱為 Follower 服務(wù)器。Leader 服務(wù)器將客戶端事務(wù)請(qǐng)求轉(zhuǎn)化成一個(gè)事務(wù) Prososal(提議),并將改 Proposal 分發(fā)給集群中所有的 Follower 服務(wù)器。之后 Leader 服務(wù)器接收了正確的反饋后,那么 Leader 就會(huì)再次向所有的 Follower 服務(wù)器分發(fā) Commit 消息,要求將前一個(gè) Proposal 提交。
這就簡(jiǎn)單闡述了ZAB 協(xié)議中消息廣播模式的部分內(nèi)容。
ZAB協(xié)議的兩種模式
ZAB 協(xié)議的包括兩種模式:崩潰恢復(fù)、消息廣播。
既然有兩種模式,那 Zookeeper 集群什么時(shí)候進(jìn)入奔潰恢復(fù)模式?什么時(shí)候進(jìn)入消息廣播模式呢?
在進(jìn)入奔潰恢復(fù)模式時(shí) Zookeeper 集群會(huì)進(jìn)行 Leader 選舉,一般有兩種情況會(huì)發(fā)生選舉:
當(dāng)服務(wù)器啟動(dòng)時(shí)期會(huì)進(jìn)行 Leader 選舉。
當(dāng)服務(wù)器運(yùn)行期 Leader 服務(wù)器的出現(xiàn)網(wǎng)絡(luò)中斷、奔潰退出、重啟等異常情況,或者當(dāng)集群中半數(shù)的服務(wù)器與該 Leader 服務(wù)器無法通信時(shí),進(jìn)入崩潰恢復(fù)模式,開始 Leader 選舉。
選舉出 Leader 服務(wù)器后,會(huì)進(jìn)入消息廣播模式,開始接收處理客戶端的請(qǐng)求,前文已經(jīng)描述,這里不再贅述。
相關(guān)名詞概念
在深入講解 ZAB 協(xié)議的兩個(gè)模式之前,先解釋 Zookeeper 的幾個(gè)相關(guān)概念,方便理解 ZAB 協(xié)議:
三種角色
在前面提到 Zookeeper 的集群中的服務(wù)器有 Leader 和 Follower ,但實(shí)際在 ZAB 協(xié)議中 Zookeeper 有三種角色,分別是 Leader、Folower、Observer,它們的分工各有不同:
-
Leader :負(fù)責(zé)整個(gè)Zookeeper 集群工作機(jī)制中的核心,主要工作有一下兩個(gè):
- 事務(wù)請(qǐng)求的唯一調(diào)度和處理者,保證集群事務(wù)處理的順序性
- 集群內(nèi)部各服務(wù)器的調(diào)度者
-
Follower :它是 Leader 的追隨者,其主要工作有三個(gè):
- 處理客戶端的非實(shí)物請(qǐng)求,轉(zhuǎn)發(fā)事務(wù)請(qǐng)求給 Leader 服務(wù)器
- 參與事務(wù)請(qǐng)求 Proposal 的投票
- 參與 Leader 選舉投票
- Observer :是 zookeeper 自 3.3.0 開始引入的一個(gè)角色,它不參與事務(wù)請(qǐng)求 Proposal 的投票,也不參與 Leader 選舉投票,只提供非事務(wù)的服務(wù)(查詢),通常在不影響集群事務(wù)處理能力的前提下提升集群的非事務(wù)處理能力。
三種狀態(tài)
在知道了 Zookeeper 中有三種角色后,不經(jīng)提問: Zookeeper 是如何知道自己目前是什么角色的呢?
在 ZAB 協(xié)議中定義:通過自身的狀態(tài)來區(qū)分自己的角色的,在運(yùn)行期間各個(gè)進(jìn)程可能出現(xiàn)以下三種狀態(tài)之一:
- LOOKING:處在這個(gè)狀態(tài)時(shí),會(huì)進(jìn)入 Leader 選舉狀態(tài)
- FOLLOWER:Follower 服務(wù)器和 Leader 服務(wù)器保持同步時(shí)的狀態(tài)
- LEADING:Leader 服務(wù)器作為主進(jìn)程領(lǐng)導(dǎo)者的狀態(tài)
在組成 ZAB 協(xié)議的所有進(jìn)程啟動(dòng)的時(shí)候,初始化狀態(tài)都是 LOOKING 狀態(tài),此時(shí)進(jìn)程組中不存在 Leader,選舉之后才有,在進(jìn)行選舉成功后,就進(jìn)入消息廣播模式,此時(shí) Zookeeper 集群中的角色狀態(tài)就不再是 LOOKING 狀態(tài)。
ZXID
前文我們知道 zookeeper 消息有嚴(yán)格的因果關(guān)系,因此必須將每一個(gè)事務(wù)請(qǐng)求按照先后順序來進(jìn)行排序與處理。那 Zookeeper 是如何保持請(qǐng)求處理的順序的呢?其中非常關(guān)鍵的點(diǎn)就是 ZXID。
那 ZXID 究竟是怎么發(fā)揮作用的呢?
Leader 服務(wù)器在接收到事務(wù)請(qǐng)求后,會(huì)為每個(gè)事務(wù)請(qǐng)求生成對(duì)應(yīng)的 Proposal 來進(jìn)行廣播,并且在廣播事務(wù) Proposal 之前,Leader 服務(wù)器會(huì)首先為這個(gè)事務(wù) Proposal 分配一個(gè)全局單調(diào)遞增的唯一 ID ,我們稱之為事務(wù) ID(即 ZXID)。
ZXID 的設(shè)計(jì)也很有特點(diǎn),是一個(gè)全局有序的 64 位的數(shù)字,可以分為兩個(gè)部分:
- 高 32 位是: epoch(紀(jì)元),代表著周期,每當(dāng)選舉產(chǎn)生一個(gè)新的 Leader 服務(wù)器時(shí)就會(huì)取出其本地日志中最大事務(wù)的 ZXID ,解析出 epoch(紀(jì)元)值操作加 1作為新的 epoch ,并將低 32 位置零。
- 低 32 位是: counter(計(jì)數(shù)器),它是一個(gè)簡(jiǎn)單的單調(diào)遞增的計(jì)數(shù)器,針對(duì)客戶端的每個(gè)事務(wù)請(qǐng)求都會(huì)進(jìn)行加 1 操作;
這里低 32 位 counter(計(jì)數(shù)器)單調(diào)遞增還好理解,高 32 位 epoch(紀(jì)元)每次選舉加 1 也許有些同學(xué)就有疑問了,為什么 epoch(紀(jì)元)每次選需要舉加 1 ,它在整個(gè) ZAB 協(xié)議中有什么作用?
我們知道每當(dāng)選舉產(chǎn)生一個(gè)新的 Leader 服務(wù)器時(shí)生成一個(gè)新的 epoch(紀(jì)元)值,而在前文我們知道,服務(wù)運(yùn)行過程中觸發(fā)選舉 Leader 的條件是:Leader 服務(wù)器的出現(xiàn)網(wǎng)絡(luò)中斷、奔潰退出、重啟等異常情況,或者當(dāng)集群中半數(shù)的服務(wù)器與該 Leader 服務(wù)器無法通信時(shí)。
這說明整個(gè) Zookeeper 集群此時(shí)處于一個(gè)異常的情況下,而在發(fā)生異常前,消息廣播進(jìn)行到哪一步驟我們根本不知道,集群中的其他 Follower 節(jié)點(diǎn)從這種崩潰恢復(fù)狀態(tài)重新選舉出 Leader 后,如果老 Leader 又恢復(fù)了連接進(jìn)入集群。此時(shí)老 Leader 的 epoch 肯定會(huì)小于新 Leader 的 epoch,這時(shí)就將老 Leader 變成 Follower,對(duì)新的 Leader 進(jìn)行數(shù)據(jù)同步。即便這時(shí)老 Leader 對(duì)其他的 Follower 節(jié)點(diǎn)發(fā)送了請(qǐng)求,F(xiàn)ollower 節(jié)點(diǎn)也會(huì)比較 ZXID 的值,因?yàn)楦?32 位加 1 了, Follower 的 epoch(紀(jì)元)大于老 Leader 的 epoch(紀(jì)元),所以 Follower 會(huì)忽略這個(gè)請(qǐng)求。
這像改朝換代一樣,前朝的劍不能斬本朝的官。
消息廣播模式
知道了這些名詞,和上文提到的零散的知識(shí)點(diǎn),其實(shí)崩潰恢復(fù)模式和消息廣播模式的過程大家大致有所了解了。
先看看消息廣播模式吧!
消息廣播的模式的過程簡(jiǎn)圖如下所示:
整個(gè)過程類似一個(gè)二階段提交的過程,但卻有所不同,ZAB 協(xié)議簡(jiǎn)化了二階段提交模型,在超過半數(shù)的 Follower 服務(wù)器已經(jīng)反饋 ACK 之后就開始提交事務(wù) Prososal 了,無需等待所有服務(wù)器響應(yīng)。
結(jié)合上圖,看看消息廣播的具體細(xì)節(jié):
- Leader 服務(wù)器接收到請(qǐng)求后在進(jìn)行廣播事務(wù) Proposal 之前會(huì)為這個(gè)事務(wù)分配一個(gè) ZXID,再進(jìn)行廣播。
- Leader 服務(wù)器會(huì)為每個(gè) Follower 服務(wù)器都各自分配一個(gè)單獨(dú)的隊(duì)列,然后將需要廣播的事務(wù) Proposal 依次放入這些隊(duì)列中去,并根據(jù) FIFO 策略進(jìn)行消息的發(fā)送。
- 每個(gè)Follower 服務(wù)器在接收到后都會(huì)將其以事務(wù)日志的形式寫入到本地磁盤中,并且在成功寫入后返回 Leader 服務(wù)器一個(gè) ACK 響應(yīng)。
- 當(dāng)有超過半數(shù)的服務(wù)器 ACK 響應(yīng)后,Leader 就會(huì)廣播一個(gè) Commit 消息給所有的 Follower 服務(wù)器,F(xiàn)ollower 接收到后就完成對(duì)事務(wù)的提交操作。
在畫一張?jiān)敿?xì)點(diǎn)的流程圖,更直觀:
這就完成了整個(gè)消息廣播了!
崩潰恢復(fù)模式
前文已經(jīng)反復(fù)提過崩潰恢復(fù)模式了,其實(shí)就是重新選舉出新的 Leader 服務(wù)器,選舉完成后 Follower 服務(wù)器在再去同步 Leader 的數(shù)據(jù)。
運(yùn)行中的服務(wù)再次進(jìn)行重新選舉,一定是出現(xiàn)某種異常,我們知道在出現(xiàn)異常情況之前 Leader 的消息廣播可能會(huì)處在任何一個(gè)階段,有可能客戶端的請(qǐng)求只是在 Leader 服務(wù)器上提出并未被提交,也可能請(qǐng)求已經(jīng)被 Leader 服務(wù)器提交。
ZAB 協(xié)議對(duì)于不同階段的出現(xiàn)的數(shù)據(jù)不一致的情況做了兼容,保證:
- 已經(jīng)在 Leader 服務(wù)器上提交的事務(wù),最終會(huì)被所有服務(wù)器都提交
- 只在 Leader 服務(wù)器上提出的事務(wù),要丟棄
針對(duì)以上的兩個(gè)要求,在進(jìn)行 Leader 選舉時(shí),之需要選舉出集群中 ZXID 最大的事務(wù) Proposal 即可,這樣就可以省去 Leader 服務(wù)器檢查 Proposal 的提交和丟棄工作了。因?yàn)?Leader 服務(wù)器的事務(wù)是最大的,一切以 Leader 服務(wù)器的數(shù)據(jù)為標(biāo)準(zhǔn)即可。
ZXID 在集群中其實(shí)并不是唯一的,所以也有可能出現(xiàn)多 Follower 服務(wù)器 ZXID 相同的情況,這時(shí)候就需要比較 Zookeeper 的 SID 值。什么是 SID?SID 是一個(gè)數(shù)字,和 zookeeper 的 myid 一致,myid 就不要解釋了,安裝過 Zookeeper 的都知道,每臺(tái)服務(wù)器都需要配置一個(gè)這樣的文件,里面只有一個(gè)數(shù)字,用來標(biāo)識(shí)這臺(tái)服務(wù)器。因?yàn)槊颗_(tái)機(jī)器的 myid 配置都不一樣,所以集群選舉的時(shí)不會(huì)出現(xiàn)相等的情況。
選舉時(shí),比較大小的源碼如下:
前面已經(jīng)說過,出現(xiàn)選舉 Leader 可能會(huì)出現(xiàn)兩種情況:
- 服務(wù)啟動(dòng)時(shí)期,發(fā)起選舉
- 服務(wù)運(yùn)行期間,出現(xiàn)異常,發(fā)起選舉
但無論是啟動(dòng)期還是運(yùn)行期進(jìn)行 Leader 選舉,其選舉過程都差不太多,我簡(jiǎn)單畫個(gè)流程圖:
<img src="https://upload-images.jianshu.io/upload_images/2710833-d87f4a39511a8630.png" alt="image.png" style="zoom:50%;" />
結(jié)合上圖,奔潰恢復(fù)模式下 Leader 選舉的過程細(xì)節(jié)如下:
- 檢測(cè)節(jié)點(diǎn)處于 LOOKING 階段,開發(fā)選舉 Leader
- 發(fā)起投票時(shí)有兩種情況:
- 在服務(wù)啟動(dòng)的初始階段,每個(gè)服務(wù)器都會(huì)投票給自己以(myid,zxid)的信息形式發(fā)送,那初始階段沒有 zxid 值,就會(huì)發(fā)送(myid,0)
- 在服務(wù)器運(yùn)行期間,每個(gè)服務(wù)器上的 zxid 都有值,且 zxid 都不相同,所以就正常發(fā)送(myid,zxid)
- 各節(jié)點(diǎn)收到信息后將收到的(myid,zxid)和自己的比較,比較的過程前面已經(jīng)說過,這里不再贅述
- 然后判斷是否有半數(shù)的機(jī)器投票選出 Leader,如果否,在進(jìn)入新一輪投票,直到選出
- 選出 Leader 后,其他節(jié)點(diǎn)就變成 Follower 角色,并向 Leader 發(fā)送自己服務(wù)器的最大 zxid ,Leader 服務(wù)器收到后會(huì)和自己本地的提議緩存隊(duì)列進(jìn)行比較,判斷使用那種策略進(jìn)行同步(后面詳細(xì)說明同步的四種策略)
- 當(dāng)同步完成,集群就可以正常的處理請(qǐng)求了,就進(jìn)入消息廣播模式了。
這就是崩潰恢復(fù)模式下選舉 Leader 的過程了!
下面再簡(jiǎn)單介紹下數(shù)據(jù)同步的四種策略,這四種同步策略保證了Zookeeper 集群中的數(shù)據(jù)一致性,也解決了前文提出的兩個(gè)問題,兼容了各種數(shù)據(jù)不一致的場(chǎng)景。
數(shù)據(jù)同步的四種策略
在數(shù)據(jù)同步之前,Leader 服務(wù)器會(huì)進(jìn)行數(shù)據(jù)同步的初始化,首先會(huì)從 Zookeeper 的內(nèi)存數(shù)據(jù)庫中提取出事務(wù)前期對(duì)應(yīng)的提議緩存隊(duì)列,同時(shí)會(huì)初始化三個(gè) ZXID 的值:
- peerLastZxid:這是 Follower 的最后處理 ZXID
- minCommittedLog:Leader 服務(wù)器的提議緩存隊(duì)列中 最小的 ZXID
- maxCommittedLog:Leader 服務(wù)器的提議緩存隊(duì)列中 最大的 ZXID
根據(jù)這三個(gè)參數(shù),就可以確定四種同步方式,分別為:
- 直接差異化同步
- 場(chǎng)景:當(dāng) minCommittedLog < peerLastZxid < maxCommittedLog 時(shí)
- 先回滾在差異化同步
- 場(chǎng)景:假如集群有 A、B、C 三臺(tái)機(jī)器,此時(shí) A 是 Leader 但是 A 掛了,在掛之前 A 生成了一個(gè)提議假設(shè)是:03,然后集群有重新選舉 B 為新的 Leader,此時(shí)生成的的提議緩存隊(duì)列為:01~02,B 和 C 進(jìn)行同步之后,生成新的紀(jì)元,ZXID 從 10 開始計(jì)數(shù),集群進(jìn)入廣播模式處理了部分請(qǐng)求,假設(shè)現(xiàn)在 ZXID 執(zhí)行到 15 這個(gè)值,此時(shí) A 恢復(fù)了加入集群,這時(shí)候就比較 A 最后提交的 ZXID:peerLastZxid 與 minCommittedLog、maxCommittedLog 的關(guān)系。此時(shí)雖然符合直接差異化同步:minCommittedLog < peerLastZxid < maxCommittedLog 這樣的關(guān)系,但是提議緩存隊(duì)列中卻沒有這個(gè) ZXID ,這時(shí)候就需要先回滾,在進(jìn)行同步。
- 僅回滾同步
- 場(chǎng)景:這里和先回滾在差異化同步類似,直接回滾就可以。
- 全量同步
- 場(chǎng)景:peerLastZxid < minCommittedLog,當(dāng)遠(yuǎn)遠(yuǎn)落后 Leader 的數(shù)據(jù)時(shí),直接全量同步。
這就是四種同步策略,這幾種同步方式也解決了上文提出的問題:
- 只在 Leader 服務(wù)器上提出的事務(wù),要丟棄(這個(gè)問題會(huì)在同步時(shí),會(huì)進(jìn)行回滾,使得只在 Leader 服務(wù)器上提出的事務(wù)丟棄)
這些就是整個(gè) ZAB 協(xié)議中崩潰恢復(fù)的內(nèi)容。
ZAB協(xié)議和Paxos算法的區(qū)別
ZAB協(xié)議看起來和Paxos有著相同之處,但它并不是Paxos的典型實(shí)現(xiàn),其實(shí)還是有一些區(qū)別,ZAB協(xié)議中額外添加了一個(gè)同步的階段,兩者設(shè)計(jì)目標(biāo)也不太一樣,ZAB協(xié)議主要用于構(gòu)建一個(gè)高可用的分布式數(shù)據(jù)主備系統(tǒng),而Paxos算法則是用于構(gòu)建一個(gè)分布式一致性的狀態(tài)機(jī)。
總結(jié)
Zookeeper 作為出色的分布式協(xié)調(diào)服務(wù),目前讀 QPS 達(dá)到 12w,出色的性能也讓開發(fā)者更加青睞,其 ZAB 協(xié)議的核心分為兩個(gè)部分:崩潰恢復(fù)、消息廣播。
典型的應(yīng)用場(chǎng)景有:
- 數(shù)據(jù)發(fā)布/訂閱、負(fù)載均衡
- 命名服務(wù)
- 分布式協(xié)調(diào)通知
- 集群管理
- Master選舉
- 分布式鎖
- 分布式隊(duì)列
- 用 Zookeeper 避免腦裂
除此之外在大數(shù)據(jù)領(lǐng)域也有應(yīng)用,例如:
- Hadoop
- HBase
- Kafka
在阿里巴巴集團(tuán)內(nèi)部實(shí)踐的 Zookeeper 的產(chǎn)品也有很多,如:
- 消息中間件:Metamorphosis
- RPC 服務(wù)框架:Dubbo
- 基于 MySQL Binlog 的增量訂閱和消費(fèi)組件:Cancel
- 分布式數(shù)據(jù)庫同步系統(tǒng):Otter
- 實(shí)時(shí)計(jì)算引擎:JStorm
歡迎關(guān)注我的個(gè)人微信公眾號(hào):java之旅