zookeeper的ZAB协议学习
文章目錄
- 1. zookeeper的復(fù)制狀態(tài)機(jī)
- 2. zookeeper的角色
- 3. zab協(xié)議的階段
- 4. zookeeper的數(shù)據(jù)模型
- 1. 在zookeeper每個(gè)服務(wù)節(jié)點(diǎn)的持久化數(shù)據(jù)
- 2. 內(nèi)存中的狀態(tài)
- 3. 選舉過程中發(fā)送的信息
- 5.消息通信機(jī)制
- 6. zab的幾個(gè)過程
- 1. leader選舉過程 zabState為ELECTION
- 2. leader確認(rèn)階段
- 3. leader的數(shù)據(jù)同步階段
- 4. 原子播報(bào)階段,這個(gè)時(shí)候使用的是二階段提交模式
- 7. 小結(jié)
- 1. 線性一致性讀
- 2. 與client的線性一致性保持
- 部分源碼,附
- 腦圖,幫助理解
- 部分參考鏈接
??看完raft的論文,再來看zookeeper關(guān)于ZAB協(xié)議的介紹,感覺raft寫的真是好,zookeeper我能夠搜索到的論文實(shí)際上都只是講了一個(gè)大概,感覺 這一篇還算是稍微講了一些東西,但是還是很不全面。迫不得已看了一下源碼,只看了leader選舉部分的實(shí)現(xiàn) FastLeaderElection,以及l(fā)eader選舉相關(guān)的工作,其實(shí)zookeeper的項(xiàng)目對(duì)于java開發(fā)人員還是很友好的,直接 git clone下來,在本地看的很爽。
下面嘗試按照raft論文的方式來對(duì)zab協(xié)議進(jìn)行闡述
1. zookeeper的復(fù)制狀態(tài)機(jī)
zk的數(shù)據(jù)存在內(nèi)存當(dāng)中(高性能),但是同時(shí)記錄操作日志+內(nèi)存快照(二進(jìn)制),持久化(類似于Redis)。
狀態(tài)機(jī)+命令日志:內(nèi)存中保存數(shù)據(jù)的最終狀態(tài),命令日志中保存所有的操作過程,內(nèi)存快照中保存某一時(shí)間節(jié)點(diǎn)的狀態(tài)機(jī)中的數(shù)據(jù)。
所以zk和raft基本一致,也是復(fù)制狀態(tài)機(jī)的工作模式,由日志復(fù)制的線性化來保證系統(tǒng)的線性化。
2. zookeeper的角色
寫的話寫leader,讀取的話可以通過FOLLOWING,OBSERVING,而且拓展OBSERVING的話可以提供更多讀的能力,但是不會(huì)降低寫入的速度。
3. zab協(xié)議的階段
??這個(gè)階段的劃分不同的論文好像有不同的說法,這里先以zookeeper的代碼中的為準(zhǔn)介紹一下,再引述一些其他的方式,在zookeeper的源碼中對(duì)zabState是這樣定義的,有4種狀態(tài)
ELECTION : leader選舉階段
DISCOVERY: leader確認(rèn)階段
SYNCHRONIZATION: 數(shù)據(jù)同步階段
BROADCAST: 原子播報(bào)階段
也有一些文章前面三個(gè)階段合起來稱為崩潰恢復(fù)階段,這種也是可以的,這種情況下zab協(xié)議就被描述為奔潰恢復(fù)和原子播報(bào)兩個(gè)階段。
4. zookeeper的數(shù)據(jù)模型
1. 在zookeeper每個(gè)服務(wù)節(jié)點(diǎn)的持久化數(shù)據(jù)
這一塊兒介紹的可能不是很全,主要關(guān)注了和選舉相關(guān)的一些數(shù)據(jù)
| logs[]: | 日志 |
| zxid : | 最后的log的zxid,這個(gè)zxid是一個(gè)64位的數(shù)字,高32位被稱為epoch,類似raft日志中的term, 低32位是遞增的counter類似于raft中的index,但是這里的counter不是全局遞增的,每次leader選舉出來之后,counter會(huì)被初始化為0,但是zxid還是全局遞增的。所以日志是全局有效的。 |
| epoch: | zxid的高32位,會(huì)單獨(dú)持久化 |
| lastCommited | 最新的commited的zxid |
2. 內(nèi)存中的狀態(tài)
| logicalclock : | 這個(gè)是選舉專用的邏輯時(shí)鐘,在服務(wù)啟動(dòng)后第一次選舉開始的時(shí)候會(huì)初始化一個(gè)FastLeaderElection實(shí)例,logicalclock是他的一個(gè)屬性,會(huì)被初始化為0;后期有可能因?yàn)橐恍┊惓T蛑亟ㄟ@個(gè)實(shí)例,默認(rèn)情況下服務(wù)不重啟,這個(gè)logicalclock會(huì)是遞增的狀態(tài),而且在zookeeper的代碼中,有些地方把這個(gè)也叫epoch或electEpoch,頗具迷惑性 |
| proposedLeader: | 當(dāng)前節(jié)點(diǎn)認(rèn)為的應(yīng)該做leader的server id,根據(jù)當(dāng)前節(jié)點(diǎn)收到的廣播消息會(huì)動(dòng)態(tài)變化,選舉剛開始的時(shí)候初始化為當(dāng)前節(jié)點(diǎn)的sid |
| proposedZxid: | 對(duì)應(yīng)的應(yīng)該做leader的server的zxid,根據(jù)當(dāng)前節(jié)點(diǎn)收到的廣播消息會(huì)動(dòng)態(tài)變化,選舉剛開始的時(shí)候初始化為當(dāng)前節(jié)點(diǎn)的zxid |
| proposedEpoch: | 對(duì)應(yīng)的應(yīng)該做leader的server的epoch,這個(gè)epoch是zxid中的epoch,但是不一定相等,因?yàn)樾碌膃poch生成了,但是包含這個(gè)epoch的zxid可能還沒有生成,,根據(jù)當(dāng)前節(jié)點(diǎn)收到的廣播消息會(huì)動(dòng)態(tài)變化,選舉剛開始的時(shí)候初始化為當(dāng)前節(jié)點(diǎn)的epoch |
| state | 每個(gè)節(jié)點(diǎn)處于的角色狀態(tài),可能是LOOKING,FOLLOWING,LEADING,OBSERVING,會(huì)隨著選舉過程逐漸變化,在節(jié)點(diǎn)啟動(dòng)或者當(dāng)前節(jié)點(diǎn)要發(fā)起leader選舉的時(shí)候是LOOKING,leader選出來后是后面三種的一種 |
| zabState | 每個(gè)節(jié)點(diǎn)處于的zab協(xié)議的階段,可能是ELECTION,DISCOVERY,SYNCHRONIZATION,BROADCAST,在節(jié)點(diǎn)啟動(dòng)或者leader選舉開始的時(shí)候初始化為ELECTION,選舉完成后epoch的確認(rèn)階段為DISCOVERY,數(shù)據(jù)同步階段為SYNCHRONIZATION,數(shù)據(jù)同步完成之后是原子播報(bào)階段,對(duì)應(yīng)的則是BROADCAST |
| Map<Long, Vote> recvset: | 用來收集looking狀態(tài)下的大家的選票信息,key是投票者的server id, Vote是對(duì)應(yīng)的server投出的票,這個(gè)map數(shù)據(jù)結(jié)構(gòu)是當(dāng)前server用來記錄同樣處于LOOKING狀態(tài)的server發(fā)出來的投票信息,如果這個(gè)達(dá)到了多數(shù)一致,那么久認(rèn)為leader選出來了。 |
| Map<Long, Vote> outofelection : | 這個(gè)對(duì)應(yīng)收集的是leader或者是follower或者leader發(fā)出來的信息,這個(gè)也是按照多數(shù)生效(也就是超過半數(shù)的leader+follower信息發(fā)過來才認(rèn)為真正找到了leader,感覺這個(gè)還是比較嚴(yán)格的),同時(shí)還會(huì)要求必須有l(wèi)eader廣播的信息認(rèn)為自己是leader. |
上面字段中的proposedLeader,proposedZxid,proposedEpoch,logicalclock是創(chuàng)建本地廣播的選票信息的主要來源(new Vote對(duì)象的時(shí)候使用到這些變量),所以我們?yōu)榱讼旅婷枋銎饋砀臃奖?#xff0c;將這些變量稱為本地選票信息。
vote的信息
| leader | 投票認(rèn)為的leader的server id |
| zxid | 認(rèn)為的leader的zxid |
| electionEpoch | 選舉的邏輯時(shí)鐘logicalclock |
| state | 投票者的server state ,一般是LOOKING |
| configData | 集群的服務(wù)器配置,用來驗(yàn)證quorum,這個(gè)字段應(yīng)該是包含了當(dāng)前集群有哪些節(jié)點(diǎn) |
| peerEpoch | 被認(rèn)為是leader的節(jié)點(diǎn)的epoch |
vote的信息是一個(gè)選票的信息,就是下面廣播的投票信息是一致的
3. 選舉過程中發(fā)送的信息
| leader | 投票認(rèn)為的leader的server id |
| zxid | 認(rèn)為的leader的zxid |
| electionEpoch | 選舉的邏輯時(shí)鐘logicalclock |
| state | 投票者的server state ,一般是LOOKING |
| configData | 集群的服務(wù)器配置,用來驗(yàn)證quorum,這個(gè)字段應(yīng)該是包含了當(dāng)前集群有哪些節(jié)點(diǎn) |
| peerEpoch | 被認(rèn)為是leader的節(jié)點(diǎn)的epoch |
5.消息通信機(jī)制
在正式了解zookeeper的zab工作模式以前有必要先簡單介紹一下zookeeper的通信方式,更加有助于理解。
6. zab的幾個(gè)過程
1. leader選舉過程 zabState為ELECTION
下面的代碼部分都在FastLeaderElection,方法lookForLeader()作為入口
如果是自己的,就直接記錄到recvset當(dāng)中,key是當(dāng)前的sid,
如果是別人的,而且n.state是LOOKING,先比較選舉邏輯時(shí)鐘electionEpoch
如果是別人的,而且n.state是OBSERVING,接著進(jìn)入步驟2,不以O(shè)BSERVING的消息為準(zhǔn),因?yàn)樗麤]有選舉權(quán)限
如果是別人的,而且n.state是FOLLOWING,LEADING,這個(gè)時(shí)候
選票信息比較規(guī)則
1.誰的peerEpoch高誰誰勝利
2.如果peerEpoch相等,則誰的zxid更大誰勝出
3.如果peerEpoch,zxid都相等,那么誰的sid大誰勝出
4.這里補(bǔ)充一個(gè)疑惑點(diǎn),為什么還要先比較peerEpoch,直接比較zxid不就行了么,因?yàn)閦xid不是包含了epoch的信息么,肯能是因?yàn)槟硞€(gè)節(jié)點(diǎn)當(dāng)選了master然后很快超時(shí)了重新選舉了?有待后續(xù)探索
2. leader確認(rèn)階段
??zabState為DISCOVERY,對(duì)應(yīng)代碼在QuorumPeer的run()方法之中,這里針對(duì)不用角色的節(jié)點(diǎn)leading,following,observing,都會(huì)有DISCOVERY階段。
??這個(gè)階段就是leader會(huì)生成新的epoch(從各個(gè)follower收集到的最大的epoch+1),并使用(epoch,0)組合生成zxid,把自己的zxid封裝成Leader.LEADERINFO包發(fā)送給發(fā)送給follower,然后follower確認(rèn)這個(gè)epoch是大于等于自己當(dāng)前看到的epoch的。如果不是就會(huì)拋異常,不承認(rèn)當(dāng)前l(fā)eader(理論上不應(yīng)該發(fā)生),如果接受了就會(huì)更新當(dāng)前服務(wù)器的epoch,封裝成Leader.LEADERINFO包發(fā)送給leader,在leader收到過半的follower的ack消息之后就說明大家都承認(rèn)他是leader了,后面就可以開始數(shù)據(jù)同步工作了。
這里的epoch一般是和zxid中的一致的,因?yàn)閒ollower的都是從master的消息當(dāng)中得到的。
3. leader的數(shù)據(jù)同步階段
??在過半follower回復(fù)了ack消息之后,leader就可以開始數(shù)據(jù)同步工作了,數(shù)據(jù)同步的時(shí)候是采用強(qiáng)leader的方式,也就是大家的數(shù)據(jù)都要和leader的對(duì)齊,這時(shí)zabState為SYNCHRONIZATION
在leader收到過半的follower的ack消息之后就認(rèn)為數(shù)據(jù)同步完成了,后面就可以進(jìn)入原子播報(bào)階段了
4. 原子播報(bào)階段,這個(gè)時(shí)候使用的是二階段提交模式
zabState為 BROADCAST
??zk的leader處理事務(wù)時(shí)FIFO機(jī)制保持了數(shù)據(jù)的一致性,這個(gè)可以保證leader上的順序性,同時(shí),leader在給follower的信息傳遞中也通過tcp的有序機(jī)制保證了follower每臺(tái)節(jié)點(diǎn)上的日志的順序一致性。所以日志可以保持全局有序性,這個(gè)和raft是一致的。
7. 小結(jié)
1. 線性一致性讀
zk的寫是具備線性一致性的,但是讀的話要分兩種情況,如果是普通的read,則不滿足線性一致性,因?yàn)樽x取沒有走zab的流程,這個(gè)時(shí)候這個(gè)請(qǐng)求可能到了某個(gè)follower,而該follower還沒有同步到這個(gè)數(shù)據(jù)的話,可能讀不到最新的數(shù)據(jù),但是這只是zookeeper對(duì)讀的一種優(yōu)化,可以更快響應(yīng),如果對(duì)數(shù)據(jù)的及時(shí)性有非常高的要求的話,那么園長(zookeeper也被稱為動(dòng)物園園長)也提供了線性一致性的讀方式,就是在真正讀取之前調(diào)用一下zk.sync()方法,這個(gè)方法會(huì)獲取當(dāng)前最大的zxid,知道本機(jī)提交這個(gè)zxid才會(huì)返回,也就保證了在這之前執(zhí)行的操作在本機(jī)都是可見的了。
2. 與client的線性一致性保持
從leader的選票比較規(guī)則以及多數(shù)投票一致性上可以得出,
事務(wù)日志
ZooKeeper Transactional Log File with dbid 0 txnlog format version 2 1/20/20 4:29:59 AM UTC session 0x30014da58050000 cxid 0x0 zxid 0x10000000c createSession 300001/20/20 4:30:00 AM UTC session 0x30014da58050000 cxid 0x4 zxid 0x10000000d create '/test_zookeeper/test/item,,v{s{31,s{'djdigest,'CgcA1GMivoBYyZZWuQDgeLuz5L45jmuVDyLKi2J0swQ=:MEonRpvlUHyT9yHsCnPddPJ0QVCMGVM5ylIV0Zv/VaY=}}},F,21/20/20 4:30:00 AM UTC session 0x30014da58050000 cxid 0x5 zxid 0x10000000e delete '/test_zookeeper/test/item1/20/20 4:30:00 AM UTC session 0x30014da58050000 cxid 0x6 zxid 0x10000000f setACL '/,v{s{31,s{'djdigest,'T9ihPbFmp0odTgrtigbbYJgBkC5Pe6XkWO543Hl1+jc=:dk8WmGqk2QsbWdyxv98BRJWaiW3xEGxpvbzVVx8z8ig=}}},21/20/20 4:30:00 AM UTC session 0x30014da58050000 cxid 0x8 zxid 0x100000010 setACL '/zookeeper,v{s{31,s{'djdigest,'V4AoltP7EqnmD6tlXT9D+yzozXnf2aN/FTmYOVekewQ=:vvEn1n8041x6LDHUgnGC/+tAAwKpFePtXZAdWP4hY3Y=}}},2部分源碼,附
/*** Messages that a peer wants to send to other peers.* These messages can be both Notifications and Acks* of reception of notification.*/public static class ToSend {/** Proposed leader in the case of notification*/ long leader;/** id contains the tag for acks, and zxid for notifications*/ long zxid;/** Epoch*/ long electionEpoch;/** Current state;*/ QuorumPeer.ServerState state;/** Address of recipient,這里是接收端的server id,實(shí)際上這個(gè)字段的信息并不會(huì)發(fā)出去*/ long sid;/** Used to send a QuorumVerifier (configuration info)*/ byte[] configData = dummyData;/** Leader epoch*/ long peerEpoch;} public class Vote {private final int version;private final long id; //leader的idprivate final long zxid; // leader的zxidprivate final long electionEpoch; // leader的邏輯時(shí)鐘logicalclockprivate final long peerEpoch; //leader的epoch}zxid的初始化
這個(gè)方法會(huì)找到當(dāng)前收到的最大epoch然后執(zhí)行+1操作得到當(dāng)前的epoch long epoch = getEpochToPropose(self.getId(), self.getAcceptedEpoch()); zk.setZxid(ZxidUtils.makeZxid(epoch, 0));epoch和clock是不是一個(gè),東西,每個(gè)選票的信息有
每一個(gè)投票者會(huì)維護(hù)一個(gè)
Map<Long, Vote> recvset = new HashMap<Long, Vote>();用來記錄自己收到的投票信息
map的key是server id,也就可以收集當(dāng)前選舉輪次中每個(gè)server的投票信息
vote的數(shù)據(jù)結(jié)構(gòu)是期望leader的id,期望leader的zxid,期望leader的投票周期(邏輯時(shí)鐘),期望leader的zxid中的epoch部分
recvset.put(n.sid, new Vote(n.leader, n.zxid, n.electionEpoch, n.peerEpoch));找到和當(dāng)前認(rèn)為的leader是一致的vote
voteSet = getVoteTracker(recvset, new Vote(proposedLeader, proposedZxid, logicalclock.get(), proposedEpoch));腦圖,幫助理解
部分參考鏈接
https://www.jianshu.com/p/90e00da6d780
https://zhouj000.github.io/2019/02/11/zookeeper-03/
https://www.cnblogs.com/leesf456/p/6140503.html
總結(jié)
以上是生活随笔為你收集整理的zookeeper的ZAB协议学习的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 算法训练营05-二叉树
- 下一篇: 算法训练营07-递归使用练习