QuorumPeer#run
QuorumPeer繼承了ZooKeeperThread線程類
org.apache.zookeeper.server.quorum.QuorumPeer#run
本地或遠(yuǎn)程注冊(cè)


在循環(huán)內(nèi)根據(jù)不同的狀態(tài)運(yùn)行

ServerState狀態(tài)有LOOKING(不清楚自己是什么節(jié)點(diǎn)需要領(lǐng)導(dǎo)者選舉)、FOLLOWING(跟隨者從節(jié)點(diǎn))、LEADING(領(lǐng)導(dǎo)者--主節(jié)點(diǎn))、OBSERVING(觀察者)
LOOKING
1、readonlymode
首先判斷只讀模式是否打開readonlymode.enabled默認(rèn)是false

2、進(jìn)行領(lǐng)導(dǎo)者選舉

org.apache.zookeeper.server.quorum.QuorumPeer#makeLEStrategy

electionAlg = FastLeaderElection 快速領(lǐng)導(dǎo)者選舉類
lookForLeader

表示:zk1、zk2、zk3三臺(tái)zk服務(wù)端(myid1=1、epoch1當(dāng)前屆數(shù)、zxid1是當(dāng)前zk1服務(wù)的狀態(tài)信息)。圖中連線0表示把投給自己的選票并放入sendqueue隊(duì)列中,圖中連線1表示發(fā)送給其他參與者(即是調(diào)用sendNotifications方法),2表示從recvqueue接收隊(duì)列獲取的選票與當(dāng)前服務(wù)器持有選票進(jìn)行比較。
org.apache.zookeeper.server.quorum.FastLeaderElection#lookForLeader

1、第一次啟動(dòng),默認(rèn)投自己,并更新當(dāng)前服務(wù)協(xié)議的領(lǐng)導(dǎo)者信息的值proposedLeader、proposedZxid、proposedEpoch

2、sendNotifications把投給自己的發(fā)送給其他服務(wù)器

3、封裝成ToSend對(duì)象放入到sendqueue發(fā)送隊(duì)列

4、從recvqueue不斷獲取收到的投票信息
其他服務(wù)器的投票或投給自己的都放到這里

這里首先會(huì)收到自己發(fā)送給自己的
會(huì)直接放入到recvset和voteSet(確認(rèn))集合中
voteSet確認(rèn)后會(huì)判斷是否超過(guò)一半,如果超過(guò)則會(huì)進(jìn)行角色設(shè)置

5、與其他服務(wù)端建立連接
①、haveDelivered是否已經(jīng)投遞
org.apache.zookeeper.server.quorum.QuorumCnxManager#haveDelivered

queueSendMap集合中值,是從sendqueue獲取的(對(duì)照領(lǐng)導(dǎo)者選舉初始化的集合和線程比較好理解),如果queueSize=0說(shuō)明里面沒(méi)有可以發(fā)送的數(shù)據(jù),說(shuō)明已經(jīng)發(fā)送過(guò)了則返回true。如果里面有值,說(shuō)明有數(shù)據(jù)沒(méi)發(fā)送出去,連接沒(méi)建立好。
如果再次從recvqueue集合得到的為null,則會(huì)進(jìn)行是否發(fā)送過(guò)。如果沒(méi)有則會(huì)與其他服務(wù)器建立連接

②、connectAll建立連接
org.apache.zookeeper.server.quorum.QuorumCnxManager#connectAll

org.apache.zookeeper.server.quorum.QuorumCnxManager#connectOne(long)

org.apache.zookeeper.server.quorum.QuorumCnxManager#connectOne

最后通過(guò)QuorumConnectionReqThread線程進(jìn)行連接處理
6、獲取的選票的狀態(tài)是LOOKING,會(huì)先對(duì)屆數(shù)Epoch比較
如果得到的其他選票信息比當(dāng)前服務(wù)器的大n.electionEpoch > logicalclock.get(),說(shuō)明得到的這個(gè)選票的機(jī)器經(jīng)歷的屆數(shù)比當(dāng)前服務(wù)器大,會(huì)先更改屆數(shù)logicalclock.set(n.electionEpoch),然后兩個(gè)投票會(huì)進(jìn)行比較。如果比當(dāng)前小,則會(huì)直接break。如果相等,也會(huì)進(jìn)行兩個(gè)投票會(huì)進(jìn)行比較


7、totalOrderPredicate
兩個(gè)投票進(jìn)行比較vote1 VS vote2。比較規(guī)則是先epoch 再zxid 最后myid
org.apache.zookeeper.server.quorum.FastLeaderElection#totalOrderPredicate


首先比較領(lǐng)導(dǎo)選舉屆數(shù)newEpoch(其他服務(wù)器獲取到的選票)與curEpoch(當(dāng)前服務(wù)器)
如果屆數(shù)Epoc相等則比較newZxid(其他服務(wù)器獲取到的選票)與curZxid(當(dāng)前服務(wù)器)
如果Zxid相等則比較newId(myid)
更新心儀的領(lǐng)導(dǎo)者,然后發(fā)送給其他服務(wù)器
8、getVoteTracker投票篩選
最終修改ack的ackset值。第一個(gè)參數(shù)投票箱recvset,這個(gè)值中的票是從其他服務(wù)器獲取的n,也是從recvqueue隊(duì)列拉取的Notification,recvset是HashMap每個(gè)sid只能是一個(gè)不能重復(fù)。第二個(gè)參數(shù)是當(dāng)前服務(wù)經(jīng)過(guò)totalOrderPredicate比較后的領(lǐng)導(dǎo)者信息。recvset集合有可能得到其他所有服務(wù)器vote2、vote3等的投票,然后與當(dāng)前服務(wù)器所比較后認(rèn)定的比較。如果相等一個(gè)就addAck一次
org.apache.zookeeper.server.quorum.FastLeaderElection#getVoteTracker

org.apache.zookeeper.server.quorum.SyncedLearnerTracker#addAck

9、過(guò)半處理hasAllQuorums

org.apache.zookeeper.server.quorum.SyncedLearnerTracker#hasAllQuorums
是否過(guò)半判斷

10、確立角色前如果能獲取到更加符合領(lǐng)導(dǎo)者的投票信息
如果通過(guò)過(guò)半機(jī)制,則會(huì)繼續(xù)從recvqueue隊(duì)列獲取。在finalizeWait = 200ms內(nèi)如果獲取到的新值比當(dāng)前更符合當(dāng)領(lǐng)導(dǎo)者則會(huì)機(jī)會(huì)繼續(xù)循環(huán)再次比較

11、身份確立
如果在指定時(shí)間finalizeWait = 200ms內(nèi)從recvqueue獲取不到值,則會(huì)進(jìn)行角色設(shè)置。確立后會(huì)把當(dāng)前投票的服務(wù)返回endVote,并賦值給QuorumPeer#currentVote
如果當(dāng)前協(xié)商的領(lǐng)導(dǎo)者與當(dāng)前myid相同則把當(dāng)前確立為領(lǐng)導(dǎo)者。

與當(dāng)前myid不相同則是FOLLOWING

領(lǐng)導(dǎo)者角色已經(jīng)確立
領(lǐng)導(dǎo)者角色已經(jīng)確立,其他服務(wù)器啟動(dòng)角色處理
recvqueue數(shù)據(jù)是從Messenger.WorkerReceiver線程不斷獲取的
1、WorkerReceiver#run
org.apache.zookeeper.server.quorum.FastLeaderElection.Messenger.WorkerReceiver#run


2、不是有效的投票者
!validVoter(response.sid)表示是觀察者
這里也進(jìn)行了處理,把當(dāng)前current的投票放入到sendqueue并返回給當(dāng)前啟動(dòng)的那臺(tái)服務(wù)器(通過(guò)response.sid)


3、是有效的投票者
①、如果當(dāng)前服務(wù)狀態(tài)是LOOKING,就是最前面分析情況。
這里會(huì)先進(jìn)行判斷n.electionEpoch < logicalclock.get()。說(shuō)明這個(gè)投票信息已經(jīng)落后,放入到sendqueue的屆數(shù)是logicalclock.get()的值。

②、如果當(dāng)前服務(wù)器狀態(tài)已經(jīng)投好票,確定了角色

把當(dāng)前服務(wù)得到的currentVote放入到sendqueue隊(duì)列,并返回發(fā)送給這臺(tái)服務(wù)器

4、FastLeaderElection#lookForLeader
領(lǐng)導(dǎo)者選舉處理
org.apache.zookeeper.server.quorum.FastLeaderElection#lookForLeader
此時(shí)從n.state得到的狀態(tài)信息就不是LOOKING,而是FOLLOWING或LEADING
這里也是進(jìn)行recvset投票箱和voteSet投票集合過(guò)濾,過(guò)半機(jī)制驗(yàn)證處理
①、屆數(shù)相等

org.apache.zookeeper.server.quorum.FastLeaderElection#checkLeader
不符合下面幾種情況都是返回true:
當(dāng)前服務(wù)器是FOLLOWING,votes(voteSet)集合中沒(méi)有LEADING那臺(tái)sid的選票,如果有votes的狀態(tài)不是LEADING
當(dāng)前服務(wù)器是LEADING,屆數(shù)不相等

②、屆數(shù)不相等
如果投票的屆數(shù)與當(dāng)前服務(wù)器屆數(shù)不相等,則會(huì)放入到outofelection集合中
n.electionEpoch != logicalclock.get()

其他情況的領(lǐng)導(dǎo)者選舉
1、FOLLOWING節(jié)點(diǎn)掛了一半
org.apache.zookeeper.server.quorum.Leader#lead

不斷向learners節(jié)點(diǎn)ping,如果掛了一半則會(huì)跳出循環(huán)leader.lead();阻塞被解除

org.apache.zookeeper.server.quorum.QuorumPeer#run
會(huì)走向finally,并updateServerState更新服務(wù)狀態(tài)

org.apache.zookeeper.server.quorum.QuorumPeer#updateServerState

QuorumPeer#run會(huì)進(jìn)行下次循環(huán)
2、LEADING節(jié)點(diǎn)掛掉
follower.followLeader();這個(gè)阻塞方法會(huì)調(diào)用finally。與FOLLOWING掛掉一半類似

總結(jié):
領(lǐng)導(dǎo)者選舉算法核心就是把選票封裝并放入到sendqueue集合發(fā)送,通過(guò)recvqueue得到其他服務(wù)節(jié)點(diǎn)的選票。并不斷比較PK,更改選票并不斷發(fā)送,并驗(yàn)證是否過(guò)半。如果過(guò)半則選舉出來(lái)領(lǐng)導(dǎo)者。
領(lǐng)導(dǎo)者選舉觸發(fā)情況:剛啟動(dòng)、FOLLOWING節(jié)點(diǎn)掛了一半、LEADING節(jié)點(diǎn)掛掉