Zookeeper ZAB协议原理浅析
文章目錄
- 前言
- 1. 基本角色和概念
- 2. Leader Election
- 3. Discovery
- 4. Synchronization
- 5. BroadCast
- 后記
前言
DTCC 要在下周一到周三要在北京舉辦,身邊有不少人都去參加了,領(lǐng)略中國最為領(lǐng)先的一些公司的自研存儲(chǔ)技術(shù)。
阿里自研polardb,polardb-x(x-engine)相關(guān),華為自研Gaussdb,開源TiDB 等,從SQL,到NoSQL,到NewSQL 都會(huì)將一些核心技術(shù)設(shè)計(jì)在大會(huì)上分享討論,而且今年也是中國數(shù)據(jù)存儲(chǔ)技術(shù)進(jìn)入時(shí)間領(lǐng)先領(lǐng)域的一年(之前都是google,微軟等巨頭),那這場(chǎng)大會(huì)將是深入了解近年來中國存儲(chǔ)最為前沿的技術(shù)盛會(huì)。
然后周五的一場(chǎng)技術(shù)分享卻發(fā)現(xiàn)自己知識(shí)體系的嚴(yán)重漏洞, 分布式領(lǐng)域從上到下(分布式協(xié)調(diào)系統(tǒng),分布式數(shù)據(jù)庫(kv,table,graph,document),分布式存儲(chǔ)(塊,文件系統(tǒng),對(duì)象),單機(jī)存儲(chǔ)),除了最底層的單機(jī)存儲(chǔ)引擎(現(xiàn)在做的)之外沒有一個(gè)能夠深入理解掌握并靈活應(yīng)用的。僅僅為了準(zhǔn)備一個(gè)分布式協(xié)調(diào)系統(tǒng)的分享,就發(fā)現(xiàn)了無數(shù)的知識(shí)漏洞(從基礎(chǔ)的編碼到上層的系統(tǒng)認(rèn)知),那這樣的基礎(chǔ)去參加更高層次的技術(shù)集會(huì)豈不是被吊打。就像平時(shí)聽大佬們分享一樣,總是在感嘆自己的無知,沒有足夠的知識(shí)基礎(chǔ),沒有辦法在大腦中快速打造屬于自己的知識(shí)架構(gòu),最后反而適得其反。
回到我們要討論的ZAB協(xié)議上,這是周內(nèi)分享的一部分,當(dāng)然僅僅是一些原理上的描述,并沒有涉及zookeeper內(nèi)部的代碼實(shí)現(xiàn)。分享過程中將ZAB協(xié)議的演進(jìn)也做了一個(gè)整體的分享,從paxos,multi paxos, raft/zab 都做了整體的描述,本篇文章主要討論zab的協(xié)議原理,畢竟zookeeper的實(shí)現(xiàn)核心,當(dāng)然要上干貨。
之前有兩篇相關(guān)的zookeeper 基礎(chǔ)入門和運(yùn)維相關(guān)的文章,能夠有效節(jié)省大家的入門時(shí)間,先對(duì) zookeeper有一個(gè)整體的了解。
1. 一文入門zookeeper
2. 一文運(yùn)維zookeeper
關(guān)于zab協(xié)議內(nèi)容的介紹能夠回答如下幾個(gè)問題:
- zookeeper 客戶端接口為什么是wait-free的?即接口之間不會(huì)相互影響,所以不需要相互等待返回,可以并行調(diào)用接口。(并行更新數(shù)據(jù))
- zookeeper 如何保證不出現(xiàn)雙主?
- zookeeper如何保證請(qǐng)求順序執(zhí)行?
1. 基本角色和概念
ZAB 協(xié)議(zookeeper atomic broadcast) zookeeper原子廣播協(xié)議,作為zookeeper實(shí)現(xiàn)分布式協(xié)調(diào)服務(wù)的核心,提供從leader 選舉,到日志復(fù)制,到數(shù)據(jù)同步 以及 最后的數(shù)據(jù)廣播 ,提供了一整套的實(shí)現(xiàn)算法。是一個(gè)值得學(xué)習(xí)研究的分布式系統(tǒng),能夠極大得幫助一些感興趣的同學(xué)提升對(duì)分布式系統(tǒng)的理解和認(rèn)知。
zookeeper 內(nèi)部有三種角色,每一種角色可以用一個(gè)zookeeper server進(jìn)程來表示:
- leader: 整個(gè)集群只能有一個(gè),主要處理寫請(qǐng)求,zookeeper中所有的寫請(qǐng)求都需要由leader負(fù)責(zé)處理。當(dāng)然也能提供讀服務(wù)。
- follower: 整個(gè)集群可以有多個(gè),只能提供讀服務(wù)。在leader發(fā)生異常之后通過ZAB的leader election 以及后續(xù)的Discovery完成leader的重新選舉,將一個(gè)follower 標(biāo)記為leader,對(duì)外提供讀寫服務(wù)。
- observer: 不參與leader選舉和投票,僅僅提供讀服務(wù)和接受leader變更的通知。可以作為zookeeper同城雙機(jī)房,中的從機(jī)房的角色。既能夠提供讀服務(wù),又能夠有效得減少兩個(gè)機(jī)房之間的rpc通信,從而提升整體的集群性能(當(dāng)然,存在的問題也很明顯,主從機(jī)房發(fā)生網(wǎng)絡(luò)分區(qū),從機(jī)房就不可用了)。
ZAB協(xié)議的主要是四個(gè)階段:
- Leader Election: 主要是節(jié)點(diǎn)之間進(jìn)行信息同步,選擇出一個(gè)leader
- Discovery: leader 獲取最新的history信息。(這里的history信息是整個(gè)集群最新的<v,zxid> 事務(wù)版本zxid以及其對(duì)應(yīng)的數(shù)據(jù))
- Synchronization: leader將獲取到的最新的數(shù)據(jù)同步到其他的從節(jié)點(diǎn),并補(bǔ)全老數(shù)據(jù),刪除新數(shù)據(jù)
- BroadCast: 之前的三個(gè)階段都是集群不可用的狀態(tài)。到了這個(gè)階段,整個(gè)集群就可以對(duì)外提供讀寫服務(wù),且zookeeper集群正常狀態(tài)下處于該階段。
ZAB 協(xié)議中有一些基本概念需要提前同步一下,如果感覺理解的還不很深刻,建議先看看前言推薦的兩篇文章。
zxid: 唯一標(biāo)識(shí)一個(gè)trasaction, 全局唯一遞增的64位整數(shù)。zxid由 <epoch, count>Epoch: 每個(gè)leader生命周期的一個(gè)標(biāo)識(shí)。newEpoch = lastEpoch + 1Count:表示每個(gè)Epoch期間發(fā)生的transaction id, 每個(gè)count 都是從0開始加一遞增- zxid的比較; 我們稱zxid <e, c> 大于 zxid’<e’, c’>,當(dāng)滿足 (e > e’ ) || (e = e’ & c > c’).
同時(shí)在ZAB中,我們前面說到的zookeeper內(nèi)部的角色都會(huì)統(tǒng)稱為peer, 一個(gè)peer代表一個(gè)角色進(jìn)程;peer中有如下幾個(gè)核心變量:
history: 被Peer提交的歷史proposal<v,zxid>,也就是數(shù)據(jù)和事務(wù)idacceptedEpoch,接受最新的NEWEPOCH的Epoch,主要是用來leader選舉過程中follower判斷是否接受leader的NEWEPOCH信息。currentEpoch接受最新的NEWLEADER的epoch, 當(dāng)選舉出來新的 leader之后,會(huì)將新leader的epoch更新到這個(gè)文件中。lastZxid: 表示history 最近提交的proposal的zxid.
這么多概念第一次看肯定記不下, 實(shí)際講解的過程中如果忘記了可以返回來查看。
2. Leader Election
顧名思義,這個(gè)階段就是選舉leader的階段,集群不可用。
核心目的:通過投票完成leader選舉,且集群每一個(gè)成員都會(huì)知道leader的epoch和leader id(myid文件)
時(shí)序圖如下:
投票的信息主要是類似vote (zxid,id),當(dāng)然實(shí)際更加詳細(xì)(vote,id,state,round);id表示 唯一標(biāo)識(shí)一個(gè)peer的id,也就是myid文件中的編號(hào);state表示peer的所處的狀態(tài)( leader,follower,election),round表示當(dāng)前peer是第幾輪投票。
上圖中我們使用node(id,zxid)來簡(jiǎn)化選舉過程,其中id就是myid, zxid就是當(dāng)前peer最新的版本號(hào)。
圖中有三條白色箭頭,分別代表三個(gè)節(jié)點(diǎn)的時(shí)間,每一個(gè)節(jié)點(diǎn)在某一個(gè)時(shí)間會(huì)有三條線。
比如T1時(shí)刻的node1的三條黃色線,表示分別向自己投票,將自己的投票信息發(fā)送給其他兩個(gè)節(jié)點(diǎn),投票信息的大小比較如下規(guī)則:node1(id,zxid) > node2(id’ , zxid’) ,當(dāng)滿足 zxid > zxid’ || (zxid==zxid’ && id > id’)
T1 時(shí)刻 開始了leader選舉,三個(gè)節(jié)點(diǎn)都將各自的node信息先發(fā)送給自己,再發(fā)送給其他兩個(gè)節(jié)點(diǎn)。
T2 時(shí)刻, 其他兩個(gè)節(jié)點(diǎn)都已經(jīng)完成了投票信息的比較:
- 比如 node1 會(huì)收到其他兩個(gè)節(jié)點(diǎn)的投票信息,依次和自己的zxid進(jìn)行比較,版本號(hào)高的成為leader;node1最高,不需要再發(fā)送消息投票(它開始已經(jīng)投自己一票了)。
- node2 會(huì)收到來自node3和node1的投票,進(jìn)行投票信息的比較,發(fā)現(xiàn)node1 > node2,node1 > node3,投票給node1,并準(zhǔn)備好 新的投票結(jié)果進(jìn)行廣播。
- node3 類似,投票給node1。
T3 時(shí)刻,整個(gè)集群其實(shí)已經(jīng)完成了選舉,node1成為新leader, 不過還是準(zhǔn)leader,后續(xù)需要進(jìn)行一些更進(jìn)一步的版本數(shù)據(jù)同步。
這個(gè)過程存在一些問題,比如node1 T1時(shí)刻發(fā)送給 node2節(jié)點(diǎn)的投票信息出現(xiàn)rpc延遲,在node2完成投票決策之后才到達(dá)node2。
在T1 時(shí)候 , node2只收到了node2自己和node3的投票,進(jìn)行投票信息的比對(duì),雖然zxid 相等,但是node3 id更大,且也滿足大多數(shù),則node2 會(huì)選擇node3作為leader,而node3會(huì)選擇node1進(jìn)行投票(node1發(fā)送到node3的投票信息并沒有延遲)也就是到T3時(shí)刻,node2認(rèn)為node3是leader , 而node3和node1都認(rèn)為自己是leader。當(dāng)然實(shí)際情況node3并不會(huì)被標(biāo)記為leader,因?yàn)閚ode3只收到一個(gè)投票,不滿足大多數(shù),只是集群中會(huì)存在這樣的沖突。
當(dāng)然這種問題在后續(xù)的Dicovery階段進(jìn)行l(wèi)eader版本信息比較時(shí)就能夠避免,發(fā)現(xiàn)leader的版本號(hào)比follower 版本號(hào)更低時(shí)會(huì)觸發(fā)重新選舉,這里說一下Leader Election這個(gè)階段如何避免 某個(gè)peer出現(xiàn) delay message的問題。
維護(hù)一個(gè)超時(shí)時(shí)間 Finalize Wait Time,當(dāng)某一個(gè)peer收到投票信息后發(fā)送了一次投票結(jié)果,但是在這段時(shí)間內(nèi)如果還收到其他的投票信息且需要變更投票結(jié)果,那么這個(gè)peer會(huì)重新發(fā)送一個(gè)新的決策結(jié)果給其他的peer。
也就是到這個(gè)Finalize Wait Time 結(jié)束后的集群leader才會(huì)是新的Leader。
T2時(shí)刻也處在FWT的時(shí)間段內(nèi),這個(gè)時(shí)候延遲的node1 的投票信息發(fā)送到了node2,發(fā)現(xiàn)之前的投票結(jié)果需要變更,則會(huì)重新發(fā)起一次投票,投票為node1作為leader。
當(dāng)然這個(gè) Finalize Wait Time 肯定也不能完全保證解決這個(gè)問題,它的數(shù)值設(shè)置多大也只是概率性的降低delay message 導(dǎo)致的投票信息延遲達(dá)到的問題。所以,還需要更加嚴(yán)謹(jǐn)?shù)臋C(jī)制來保證不出現(xiàn)leader沖突的問題,我們繼續(xù)來看后續(xù)階段。
3. Discovery
Leader Election之后整個(gè)集群已經(jīng)完成了選主,當(dāng)然這個(gè)leader并不是真正的leader。之前它可能擁有最新的版本號(hào),但現(xiàn)在已經(jīng)改朝換代了,需要有自己的年號(hào)來向天下宣告自己的登基。所以它需要重新發(fā)布年號(hào)(版本號(hào)),同時(shí)需要掌控整個(gè)王朝最新的資源(最新的數(shù)據(jù)),只有完成這一些事情自己才能穩(wěn)坐寶座,成為正王。
現(xiàn)在的集群擁有這個(gè)幾個(gè)角色,其中有一個(gè)準(zhǔn)leader(按照之前l(fā)eader選舉過程,也有可能出現(xiàn)雙leader,然后進(jìn)入這個(gè)階段)
ZAB協(xié)議的角度先總體說一下這個(gè)階段的核心目的:
- 確認(rèn)/生成 一個(gè)新leader的Epoch
- 新的Epoch同步到所有的Follower
- Leader獲取最新的history, 準(zhǔn)備進(jìn)行后續(xù)的Synchronization 階段。history: <value, zxid>
具體過程如下:
-
Follower節(jié)點(diǎn)知道準(zhǔn)Leader節(jié)點(diǎn)之后,會(huì)發(fā)送一個(gè)
FOLLOWERINFO的信息攜帶自己的f.acceptedEpoch內(nèi)容 -
準(zhǔn)leader節(jié)點(diǎn)收到超過半數(shù)的
FOLLOWERINFO之后,會(huì)從中選擇一個(gè)最大的,并在最大的基礎(chǔ)上+1,即max{f.acceptedEpoch} + 1 -
準(zhǔn)Leader將準(zhǔn)備好的
NEWEPOCH發(fā)送到follower, 表示自己的年號(hào)已經(jīng)更新。等待quorum中的成員回復(fù)ACK -
follower 收到
NEWEPOCH之后和自己本地epoch進(jìn)行比對(duì):a. leader 發(fā)送過來的epoch > acceptedEpoch,更新自己的acceptedEpoch 為新的epoch,并回復(fù)一個(gè)ACKEPOCH消息,這個(gè)消息中攜帶上個(gè)currentEpoch, history 和 lastZxid(history 最近提交的proposal的zxid)
b. leader發(fā)送過來的epoch < acceptedEpoch ,則 回退到階段0,重新進(jìn)行l(wèi)eader選舉(集群中存在節(jié)點(diǎn)異常)。
c. leader發(fā)送過來的epoch 和 本地acceptedEpoch相等的場(chǎng)景論文并沒有提到,感覺應(yīng)該會(huì)需要重新選舉,畢竟leader已經(jīng)在收到大多數(shù)的FOLLOWERINFO 中最大的+1了。 -
Leader收到所有quorum中follower的ACKEPOCH, 從所有的消息中找出currentEpoch最大的或者lastZxid最大的follower,然后把該follower的history 作為自己的history(pull history的過程)。當(dāng)然,如果本地自己的currentEpoch 或者 lastZxid最大,那就用本地的history即可。
到現(xiàn)在,Leader 已經(jīng)獲取到最新的history, 并開始準(zhǔn)備進(jìn)行后續(xù)的Synchronization 階段。
4. Synchronization
這個(gè)階段的核心目的是:
- 同步history proposal。即將Leader獲取到的最新的history 數(shù)據(jù)同步到follower節(jié)點(diǎn),讓整個(gè)集群數(shù)據(jù)對(duì)齊。
- 處理上個(gè)階段遺留下來的proposal,follower節(jié)點(diǎn)中的數(shù)據(jù) 需要清理的可以清理,需要?jiǎng)h除的可以刪除。
大體過程如下:
Leader這個(gè)階段剛開始的時(shí)候已經(jīng)有了整個(gè)集群最新的history數(shù)據(jù)。
-
Leader 想所有的follower發(fā)送
NEWLEADER信息,其中包括leader自己最新的epoch 和 最新的history數(shù)據(jù)。 -
follower 收到leader的消息之后判斷當(dāng)前輪次自己的acceptedEpoch和leader發(fā)送過來的epoch是否一樣(discovery階段已經(jīng)對(duì)follower自己的acceptedEpoch進(jìn)行了更新)
1). follower的acceptedEpoch和新epoch相同,表示自己已經(jīng)跟上了新的epoch, 那么做如下幾個(gè)事情
a. 更新自己的currentEpoch為新的epoch,表示進(jìn)入新的朝代了
b. 按照zxid的大小逐一進(jìn)行本地proposed,此時(shí)這些transaction還未commit。
c. 更新自己的history為最新的history
d. 返回一個(gè)ACKNEWLEADER 給leader, 表示這個(gè)follower已經(jīng)完成數(shù)據(jù)同步2). follower收到的epoch和本地的acceptedEpoch不同,那么回退到階段0,重新選主(存在節(jié)點(diǎn)異常,當(dāng)前主節(jié)點(diǎn)并不能包含所有的數(shù)據(jù),不能隨意更新,否則會(huì)丟數(shù)據(jù))。
-
Leader 收到follower節(jié)點(diǎn)的ACKNEWLEADER消息之后,對(duì)proposal的數(shù)據(jù)進(jìn)行提交commit,所有的follower節(jié)點(diǎn)也會(huì)收到commit請(qǐng)求(落盤)
-
follower節(jié)點(diǎn)收到leader的COMMIT請(qǐng)求,會(huì)對(duì)自己本地已經(jīng)proposed但還未commit的事務(wù),按照zxid進(jìn)行從小到大的排序,優(yōu)先commit zxid較小的節(jié)點(diǎn)。
-
Leader 和 Follower都完成同步之后進(jìn)入第四階段。
從朝代更替來看,前面的幾個(gè)階段整個(gè)國家處于亂世:無君,君臣各有所思,各有所謀,可能的多君。。。。
到這個(gè)階段,歷經(jīng)千難萬險(xiǎn)完成了國家統(tǒng)一,君強(qiáng)臣明,整個(gè)國家開始一致對(duì)外,共向繁榮的場(chǎng)景。
正如我們的春秋戰(zhàn)國到秦,五代十國到宋,南宋到元,每一個(gè)帝國的崛起都?xì)v經(jīng)無數(shù)次的嘗試和磨難,但大一統(tǒng)的目標(biāo)從秦遍成了唯一,只有集群大一統(tǒng),才能夠更好得施展每一個(gè)角色的才華。
5. BroadCast
這是一個(gè)穩(wěn)定的時(shí)代, 之前三個(gè)階段,zookeeper無法對(duì)外提供服務(wù)。到了這個(gè)階段,整個(gè)集群即能夠?qū)ν馓峁┳x寫服務(wù)。
這個(gè)階段如果發(fā)生集群成員變更,即加入了follower和observer。
整體過程如下:
- Leader收到一個(gè)寫請(qǐng)求,會(huì)生成一個(gè)Proposal: <value, zxid>, zxid = lastZxid + 1,對(duì)quorum中的follower節(jié)點(diǎn)發(fā)起propose請(qǐng)求,并攜帶生成的Proposal。
- follower節(jié)點(diǎn)收到propose的proposal,將其加入到history隊(duì)列,并向leader回復(fù)ACK,表示已經(jīng)收到propsal。
- leader收到過半節(jié)點(diǎn)的ACK之后,認(rèn)為可以進(jìn)行commit,則向quorum發(fā)送COMMIT請(qǐng)求
- follower收到propose的commit 之后開始進(jìn)行提交
1). 為了滿足zxid的全局一致性,這里會(huì)檢查follower本地是否有未提交的proposal<v,z>,保證比當(dāng)前zxid小的propose先提交
2). 當(dāng)所有小于zxid的propose都完成commit之后再提交當(dāng)前的zxid。 - BroadCast階段 也能夠接受新的Follower或者Observer的加入,步驟如下:
1). 新加入的節(jié)點(diǎn)會(huì)給Leader發(fā)送一個(gè)FOLLOWERINFO信息
2). Leader收到后會(huì)回復(fù)給他一個(gè)NEWEPOCH 和 NEWLEADER, 告訴這個(gè)節(jié)點(diǎn)集群最新的epoch和history數(shù)據(jù)
3). 新節(jié)點(diǎn)收到NEWLEADER后,如果正常邏輯處理完成后(將history中的數(shù)據(jù)并發(fā)propose),回一個(gè)ACKNEWLEADER給Leader
4). Leader收到ACK回復(fù)之后告訴他可以進(jìn)行本地proposal的提交了,會(huì)發(fā)送一個(gè)COMMIT 請(qǐng)求
5). 新節(jié)點(diǎn)收到這個(gè)請(qǐng)求,對(duì)本地完成proposed的數(shù)據(jù)按照zxid從小到達(dá)進(jìn)行commit(落盤)。
6). 新節(jié)點(diǎn)完成commit之后,leader會(huì)將新的節(jié)點(diǎn)加入到自己的quorum列表中。
ps :
a. 以上過程不論是leader還是follower 節(jié)點(diǎn)在進(jìn)行propose的過程都是可以并發(fā)進(jìn)行的。對(duì)于leader來說,一個(gè)proposal的發(fā)起不會(huì)等待上一個(gè)commit完成之后才會(huì)發(fā)起,當(dāng)前proposal和上一個(gè)proposal是可以并行處理,保證了zookeeper的更新接口可以提供wait-free 的能力。b. commit 時(shí)需要保證本地比當(dāng)前zxid更小的事務(wù)優(yōu)先提交,從而保證zookeeper的Linearizable 特性。
以上基本就是ZAB協(xié)議的每個(gè)階段的細(xì)節(jié),在我們實(shí)際zookeeper的實(shí)現(xiàn)中,會(huì)對(duì)以上四個(gè)階段做優(yōu)化。
我們能夠看到從開始選主到能夠提供服務(wù),這個(gè)過程還是會(huì)有大量的rpc 和數(shù)據(jù)交互,zookeeper實(shí)際將leader election和discovery變更為 FLE(Fast Leader Election)階段,在完成Leader 選舉之后 leader 就已經(jīng)擁有了最新的history數(shù)據(jù)。
將Synchronization 階段變更為了Recovery 過程,整體上就是讓單次rpc攜帶的數(shù)據(jù)量更大,能夠在完成相互的交流通信之后進(jìn)行更多更快的本地計(jì)算,而不是將較多的時(shí)間消耗在rpc和等待rpc數(shù)據(jù)的過程中。
后記
這是一個(gè)亂世的結(jié)束,但從歷史的角度看,也會(huì)是一個(gè)亂世的開始。
居安思危很難,領(lǐng)導(dǎo)層 只能夠在大多數(shù)場(chǎng)景做出正確的決策,但p9999和max之間差異還是太大。我們的系統(tǒng) 同樣是一個(gè)復(fù)雜體系,大量的靜默錯(cuò)誤(硬件損耗,磁盤的bit位反轉(zhuǎn),還有大量的底層系統(tǒng)軟件到上層應(yīng)用軟件的bug)無法保證一個(gè)分布式集群 每時(shí)每刻都正常運(yùn)行。只能在有限的人力,有限的資源下最大化我們系統(tǒng)的可用性,創(chuàng)造足夠的價(jià)值。正如那一些歷史上的偉大帝國 在他們所在的時(shí)代創(chuàng)造了讓后人敬仰的文明。
總結(jié)
以上是生活随笔為你收集整理的Zookeeper ZAB协议原理浅析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Go 分布式学习利器(16) -- go
- 下一篇: Go 语言实现字符串匹配算法 -- BF