日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ZAB协议选主过程详解

發(fā)布時(shí)間:2024/4/18 编程问答 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ZAB协议选主过程详解 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

說明

ZAB 協(xié)議是為分布式協(xié)調(diào)服務(wù)ZooKeeper專門設(shè)計(jì)的一種支持崩潰恢復(fù)的一致性協(xié)議。基于該協(xié)議,ZooKeeper 實(shí)現(xiàn)了一種主從模式的系統(tǒng)架構(gòu)來保持集群中各個(gè)副本之間的數(shù)據(jù)一致性。

ZAB協(xié)議運(yùn)行過程中,所有的客戶端更新都發(fā)往Leader,Leader寫入本地日志后再復(fù)制到所有的Follower節(jié)點(diǎn)。

一旦Leader節(jié)點(diǎn)故障無法工作,ZAB協(xié)議能夠自動(dòng)從Follower節(jié)點(diǎn)中重新選擇出一個(gè)合適的替代者,這個(gè)過程被稱為選主,選主也是ZAB協(xié)議中最為重要和復(fù)雜的過程。本文主要描述ZAB協(xié)議的選主過程以及在Zookeeper中的實(shí)現(xiàn)。

選主時(shí)機(jī)

節(jié)點(diǎn)啟動(dòng)時(shí)

每個(gè)節(jié)點(diǎn)啟動(dòng)的時(shí)候狀態(tài)都是LOOKING,處于觀望狀態(tài),接下來就是要進(jìn)行選主了。

Leader節(jié)點(diǎn)異常

Leader節(jié)點(diǎn)運(yùn)行后會(huì)周期性地向Follower發(fā)送心跳信息(稱之為ping),如果一個(gè)Follower未收到Leader節(jié)點(diǎn)的心跳信息,Follower節(jié)點(diǎn)的狀態(tài)會(huì)從FOLLOWING轉(zhuǎn)變?yōu)長(zhǎng)OOKING,再次進(jìn)入選主階段,如下:

在Follower節(jié)點(diǎn)的主要處理流程中:

void followLeader() throws InterruptedException { try {......while (this.isRunning()) {readPacket(qp);processPacket(qp);}// 如果上面的while循環(huán)內(nèi)出現(xiàn)異常// 注意:長(zhǎng)時(shí)間沒有收到Leader的消息也是異常 } catch (Exception e) {// 出現(xiàn)異常就退出了while循環(huán)// 也就結(jié)束了Follower的處理流程 }

接下來進(jìn)入節(jié)點(diǎn)運(yùn)行的主循環(huán):

public void run() {while (running) {switch (getPeerState()) {case FOLLOWING:try {setFollower(makeFollower(logFactory));follower.followLeader();} catch (Exception e) {......} finally {follower.shutdown();setFollower(null);// 狀態(tài)更新為L(zhǎng)OOKINGupdateServerState();}break;......} }

此后,該Follower就會(huì)再次進(jìn)入選主階段。

多數(shù)Follower節(jié)點(diǎn)異常

Leader節(jié)點(diǎn)也會(huì)檢測(cè)Follower節(jié)點(diǎn)的狀態(tài),如果多數(shù)Follower節(jié)點(diǎn)不再響應(yīng)Leader節(jié)點(diǎn)(可能是Leader節(jié)點(diǎn)與Follower節(jié)點(diǎn)之間產(chǎn)生了網(wǎng)絡(luò)分區(qū)),那么Leader節(jié)點(diǎn)可能此時(shí)也不再是合法的Leader了,也必須要進(jìn)行一次新的選主。

Leader節(jié)點(diǎn)啟動(dòng)時(shí)會(huì)接收Follower的主動(dòng)連接請(qǐng)求,對(duì)于每一個(gè)Follower的新連接,Leader會(huì)創(chuàng)建一個(gè)LearnerHandler對(duì)象來處理與該Follower的消息通信。

LearnerHandler創(chuàng)建一個(gè)獨(dú)立線程,在主循環(huán)內(nèi)不停地接受Follower的消息并根據(jù)消息類型決定如何處理。除此以外,每收到Follower的消息時(shí),便更新下一次消息的過期時(shí)間,在LeaderHandler::run()

public void run() {......while (true) {qp = new QuorumPacket();ia.readRecord(qp, "packet");......// 收到Follower的消息后// 設(shè)置下一個(gè)消息的過期時(shí)間tickOfNextAckDeadline = leader.self.tick.get() + leader.self.syncLimit;......}...... }

在Leader節(jié)點(diǎn)的主循環(huán)流程中,會(huì)判斷多數(shù)派節(jié)點(diǎn)的消息狀態(tài),如下:

void lead() throws IOException, InterruptedException {......while (true) {......// 判斷每個(gè)每個(gè)Follower節(jié)點(diǎn)的狀態(tài)// 是否與Leader保持同步for (LearnerHandler f : getLearners()) {if (f.synced()) { syncedAckSet.addAck(f.getSid());}}......}if (!tickSkip && !syncedAckSet.hasAllQuorums()) {// 如果失去了大多數(shù)Follower節(jié)點(diǎn)的認(rèn)可,就跳出Leader主循環(huán),進(jìn)入選主流程break;}...... }// LearnerHandler::synced()邏輯 // 即判斷當(dāng)前是否已經(jīng)過了期望得到的Follower的下一個(gè)消息的期限:tickOfNextAckDeadline public boolean synced() {return isAlive() && leader.self.tick.get() <= tickOfNextAckDeadline; }

選主流程

在描述詳細(xì)的選主過程之前,有必要交代一些概念,以便對(duì)接下來的大段文字不會(huì)有丈二和尚的感覺。

election epoch

這是分布式系統(tǒng)中極其重要的概念,由于分布式系統(tǒng)的特點(diǎn),無法使用精準(zhǔn)的時(shí)鐘來維護(hù)事件的先后順序,因此,Lampert提出的Logical Clock就成為了界定事件順序的最主要方式。

分布式系統(tǒng)中以消息標(biāo)記事件,所謂的Logical Clock就是為每個(gè)消息加上一個(gè)邏輯的時(shí)間戳。在ZAB協(xié)議中,每個(gè)消息都被賦予了一個(gè)zxid,zxid全局唯一。zxid有兩部分組成:高32位是epoch,低32位是epoch內(nèi)的自增id,由0開始。每次選出新的Leader,epoch會(huì)遞增,同時(shí)zxid的低32位清0。這其實(shí)像極了咱們古代封建王朝的君主更替,每一次的江山易主,君王更替。

zxid

每個(gè)消息的編號(hào),在分布式系統(tǒng)中,事件以消息來表示,事件發(fā)生的順序以消息的編號(hào)來標(biāo)記。在ZAB協(xié)議中,這就是zxid。ZAB協(xié)議中,消息的編號(hào)只能由Leader節(jié)點(diǎn)來分配,這樣的好處是我們就可以通過zxid來準(zhǔn)確判斷事件發(fā)生的先后,記住,是任意事件,這也是分布式系統(tǒng)中,由全局唯一的主節(jié)點(diǎn)來處理更新事件帶來的極大好處。

分布式系統(tǒng)運(yùn)行的過程中,Leader節(jié)點(diǎn)必然會(huì)發(fā)生改變,一致性協(xié)議必須能夠正確處理這種情況,保證在Leader發(fā)生變化的時(shí)候,新的Leader期間,產(chǎn)生的zxid必須要大于老的Leader時(shí)生成的zxid。這就得通過上面說的epoch機(jī)制了,具體實(shí)現(xiàn)會(huì)在下面的選主過程中詳細(xì)描述。

LOOKING/FOLLOWING/LEADING

這三者描述了系統(tǒng)中節(jié)點(diǎn)的狀態(tài)

  • LOOKING: 節(jié)點(diǎn)正處于選主狀態(tài),不對(duì)外提供服務(wù),直至選主結(jié)束;
  • FOLLOWING: 作為系統(tǒng)的從節(jié)點(diǎn),接受主節(jié)點(diǎn)的更新并寫入本地日志;
  • LEADING: 作為系統(tǒng)主節(jié)點(diǎn),接受客戶端更新,寫入本地日志并復(fù)制到從節(jié)點(diǎn)

選主過程

選主過程參與方有:

  • 發(fā)起選主的節(jié)點(diǎn)
  • 集群其他節(jié)點(diǎn),這些節(jié)點(diǎn)會(huì)為發(fā)起選主的節(jié)點(diǎn)進(jìn)行投票

節(jié)點(diǎn)B判斷確定A可以成為主,那么節(jié)點(diǎn)B就投票給節(jié)點(diǎn)A,判斷的依據(jù)是:

election epoch(A) > election epoch (B)

zxid(A) > zxid(B)

sid(A) > sid(B)

選主流程:

  • 候選節(jié)點(diǎn)A初始化自身的zxid和epoch:updateProposal();
  • 向其他所有節(jié)點(diǎn)發(fā)送選主通知:sendNotifications();
  • 等待其他節(jié)點(diǎn)的回復(fù):recvqueue.poll();
  • 如果來自B節(jié)點(diǎn)的回復(fù)不為空,且B是一個(gè)有效節(jié)點(diǎn),判斷B此時(shí)的運(yùn)行狀態(tài)是LOOKING(也在發(fā)起選主)還是LEADING/FOLLOWING(正常請(qǐng)求處理過程)
  • 情況1:投票節(jié)點(diǎn)是LOOKING狀態(tài)

    以下以圖示法說明此時(shí)選主過程:

    STEP 1:處于LOOKING狀態(tài)的A發(fā)起一次選主請(qǐng)求,并將請(qǐng)求廣播至B、C節(jié)點(diǎn),而此時(shí)B、C也恰好處于LOOKING狀態(tài):

    STEP 2B、C節(jié)點(diǎn)處理A的選主消息,其中,B接受A的提議,C拒絕A的提議:

    說明:

    • 伴隨著A的選主消息的一個(gè)額外收獲是B和C此時(shí)都獲得了A節(jié)點(diǎn)選主的結(jié)果(A投票給,記錄為<A, A>),記錄該信息,作為后續(xù)判斷大家是否達(dá)成一致的標(biāo)準(zhǔn)。

    STEP 3B將處理結(jié)果通知A、C;C由于拒絕了A的提議,因此,無需擴(kuò)散消息。

    說明:

    • 因?yàn)锽更新了自己的投票,從投票給自己變成投票給A,因此根據(jù)協(xié)議的定義,需要將該消息擴(kuò)散出去。而C由于拒絕了A的提議,因此,無需擴(kuò)散消息;
    • B將消息擴(kuò)散給A和C的同時(shí),A和C也就了解了B的投票信息,可以更新本地的投票信息表,例如上面經(jīng)過B的擴(kuò)散后,A知道了B節(jié)點(diǎn)的投票信息,C知道了A和B節(jié)點(diǎn)的投票信息。

    STEP 4C同時(shí)也發(fā)起選主

    STEP 5A、B分別處理C的選主請(qǐng)求

    說明:

    • 這里A和B判斷得出C是最合適的Leader,因此A和B都更新自己的候選Leader為C,同時(shí)由于C的消息,A和B都更新自身維護(hù)的投票信息,增加C的投票信息。

    STEP 6A、B將更新后的信息擴(kuò)散到其他節(jié)點(diǎn)

    說明:

    • 因?yàn)樵诘谖宀街蠥和B分別將自己的候選Leader變成了C,因此需要將該信息通知到其他節(jié)點(diǎn),其他節(jié)點(diǎn)在收到新的投票信息后會(huì)更新本地的投票信息列表,如上圖。

    STEP 7: 選主結(jié)束

    此時(shí)此刻,所有的節(jié)點(diǎn)都已經(jīng)達(dá)成了一致:每個(gè)節(jié)點(diǎn)都同意節(jié)點(diǎn)C作為新的Leader。

    情形2:投票節(jié)點(diǎn)是FOLLOWING/LEADING狀態(tài)

    以下原因可能導(dǎo)致出現(xiàn)這種情況:

    • 節(jié)點(diǎn)A(Follower)與Leader出現(xiàn)網(wǎng)絡(luò)問題而觸發(fā)一次選主,但是其他Follower與Leader正常;
    • 新節(jié)點(diǎn)加入集群也會(huì)有同樣的情況發(fā)生。

    如果一個(gè)正常服務(wù)狀態(tài)(LEADING/FOLLOWING)的節(jié)點(diǎn)收到一個(gè)節(jié)點(diǎn)的選主請(qǐng)求,處理流程是怎么樣的呢?

    在QuorumPeer對(duì)象中存在一個(gè)WorkerReceiver線程,該線程的主要作用是接受其他節(jié)點(diǎn)發(fā)送過來的選主消息變更的通知。這個(gè)線程中收到其他節(jié)點(diǎn)發(fā)來的選主消息通知時(shí)會(huì)判斷當(dāng)前節(jié)點(diǎn)的狀態(tài): public void run() {......if(self.getPeerState() == QuorumPeer.ServerState.LOOKING) {recvqueue.offer(n);......// 如果節(jié)點(diǎn)處于非LOOKING狀態(tài)} else {// 節(jié)點(diǎn)的投票信息Vote current = self.getCurrentVote();if(ackstate == QuorumPeer.ServerState.LOOKING) {QuorumVerifier qv = self.getQuorumVerifier();// 給發(fā)起投票的節(jié)點(diǎn)返回當(dāng)前節(jié)點(diǎn)的投票信息ToSend notmsg = new ToSend(ToSend.mType.notification, ...)sendqueue.offer(notmsg);}} }

    此時(shí)處理過程是怎樣的:

    • 如果Logical Clock相同,將數(shù)據(jù)保存在recvset,如果Sender宣稱自己是Leader,那么判斷是不是半數(shù)以上的服務(wù)器都選舉它,如果是設(shè)置角色并退出選舉。
    • 否則,這是一條與當(dāng)前LogicalClock不符合的消息,說明在另一個(gè)選舉過程中已經(jīng)有了選舉結(jié)果(另一個(gè)選舉過程指的是什么),于是將該選舉結(jié)果加入到OutOfElection集合中,根據(jù)OutOfElection來判斷是否可以結(jié)束選舉,如果可以也是保存LogicalClock,更新角色,退出選舉。出現(xiàn)這種情況可能是由于原集群中有一個(gè)新的服務(wù)器上線/重新啟動(dòng),但是原來的已有集群的機(jī)器已經(jīng)選主成功,因此,別無他法,只有加入原來的集群成為Follower。但這里的Logical Clock不符合,可能大也可能小,怎么理解?

    說明:

    • logical clock相同可能是因?yàn)槌霈F(xiàn)這種情況:A、B同時(shí)發(fā)起選主,此時(shí)他們的election epoch可能相同,如果B率先完成了選主過程(B可能變成了Leader,也有可能B選擇了其他節(jié)點(diǎn)為L(zhǎng)eader),但是A還在選主過程中,此時(shí)如果B收到了A的選主消息,那么B就將自己的選主結(jié)果和自己的狀態(tài)(LEADING/FOLLOWING)連同自己的election epoch回復(fù)給A,對(duì)于A來說,它收到了一個(gè)來自選主完成的節(jié)點(diǎn)B的election epoch相同的回復(fù),便有了上面的第一種情況;

    ?

    上圖的10表示選主的Logical Clock

    • logical clock不相同可能是因?yàn)樾略隽艘粋€(gè)節(jié)點(diǎn)或者某個(gè)節(jié)點(diǎn)出現(xiàn)了網(wǎng)絡(luò)隔離導(dǎo)致其觸發(fā)一次新的選主,然后系統(tǒng)中其他節(jié)點(diǎn)狀態(tài)依然正常,此時(shí)發(fā)起選主的節(jié)點(diǎn)由于要遞增其logical clock,必然會(huì)導(dǎo)致其logical clock要大于其他正常節(jié)點(diǎn)的logical clock(當(dāng)然也可能小于,考慮一個(gè)新上線節(jié)點(diǎn)觸發(fā)選主,其logical clock從1開始計(jì)算)。因此就出現(xiàn)了上面的第二種情況,如下圖:

    如果對(duì)方節(jié)點(diǎn)處于FOLLOWING/LEADING狀態(tài),除檢查是否過半外,同時(shí)還要檢查leader是否給自己發(fā)送過投票信息,從投票信息中確認(rèn)該leader是不是LEADING狀態(tài)。這個(gè)解釋如下:

    因?yàn)槟壳發(fā)eader和follower都是各自檢測(cè)是否進(jìn)入leader選舉過程。leader檢測(cè)到未過半的server的ping回復(fù),則leader會(huì)進(jìn)入LOOKING狀態(tài),但是follower有自己的檢測(cè),感知這一事件,還需要一定時(shí)間,在此期間,如果其他server加入到該集群,可能會(huì)收到其他follower的過半的對(duì)之前l(fā)eader的投票,但是此時(shí)該leader已經(jīng)不處于LEADING狀態(tài)了,所以需要這么一個(gè)檢查來排除這種情況。

    Leader/Follower信息同步

    選出了Leader還不算完,根據(jù)ZAB協(xié)議定義,在真正對(duì)外提供服務(wù)之前還需要一個(gè)信息同步的過程。具體來說,Leader和Follower之間需要同步以下信息:

    • 下一次zxid:這是因?yàn)檫x出新的Leader后,epoch勢(shì)必發(fā)生改變,因此,需要經(jīng)過多方協(xié)商后選擇出當(dāng)前最大的epoch,然后再拼湊出下一輪提供服務(wù)的zxid
    • 日志內(nèi)容:ZAB使用日志同步來維護(hù)多個(gè)節(jié)點(diǎn)的一致性狀態(tài),同步過程是由Leader發(fā)往Follower,因此可能會(huì)存在大家步調(diào)不一致的情況,表現(xiàn)出的現(xiàn)象就是節(jié)點(diǎn)日志內(nèi)容不同,可能某些節(jié)點(diǎn)領(lǐng)先,而某些節(jié)點(diǎn)落后。

    Epoch協(xié)商

    選主過程結(jié)束后,接下來就是多數(shù)派節(jié)點(diǎn)協(xié)商出一個(gè)最大的epoch(但如果是采用FastLeaderElection算法的話,選出來的Leader其實(shí)就擁有了最大的epoch)。

    這個(gè)過程涉及到Leader和Follower節(jié)點(diǎn)的通信,具體流程:

  • Leader節(jié)點(diǎn)啟動(dòng)時(shí)調(diào)用getEpochToPropose(),并將自己的zxid解析出來的epoch作為參數(shù);
  • Follower節(jié)點(diǎn)啟動(dòng)時(shí)也會(huì)連接Leader,并從自己的最后一條zxid解析出epoch發(fā)送給Leader,leader中處理該Follower消息的線程同樣調(diào)用getEpochToPropose(),只是此時(shí)傳入的參數(shù)是該Follower的epoch;
  • getEpochToPropose()中會(huì)判斷參數(shù)中傳入的epoch和當(dāng)前最大的epoch,選擇兩者中最大的,并且判斷該選擇是否已經(jīng)獲得了多數(shù)派的認(rèn)可,如果沒有得到,則阻塞調(diào)用getEpochToPropose()的線程;如果獲得認(rèn)可,那就喚醒那些等待epoch協(xié)商結(jié)果的線程,于是,Follower就得到了多數(shù)派認(rèn)可的全新的epoch,大家就從這個(gè)epoch開始生成新的zxid;
  • Leader的發(fā)起epoch更新過程在函數(shù)Leader::lead()中,Follower的發(fā)起epoch更新過程在函數(shù)Follower::followLeader()中,Leader處理Follower的epoch更新請(qǐng)求在函數(shù)LearnerHandler::run()中。
  • 日志同步

    選主結(jié)束后,接下來需要在Leader和Follower之間同步日志,根據(jù)ZAB協(xié)議定義,這個(gè)同步過程可能是Leader流向Follower。

    對(duì)比的原理是將Follower的最新的日志zxid和Leader的已經(jīng)提交的日志zxid對(duì)比,會(huì)有以下幾種可能:

    • 如果Leader的最新提交的日志zxid比Follower的最新日志的zxid大,那就將多的日志發(fā)送給Follower,讓他補(bǔ)齊
    • 如果Leader的最新提交的日志zxid比Follower的最新日志的zxid小,那就發(fā)送命令給Follower,將其多余的日志截?cái)?/span>;
    • 如果兩者恰好一樣,那什么都不用做。

    即使是一個(gè)日志同步過程也要經(jīng)歷以下幾個(gè)同步過程:

  • Leader發(fā)送同步日志給Follower,該過程傳輸?shù)闹饕侨罩緮?shù)據(jù)流或者Leader給Follower的各種命令;
  • Leader發(fā)送NEWLEADER命令給Follower,該命令的作用應(yīng)該是告訴Follower日志同步已經(jīng)完成,Follower對(duì)該NEWLEADER作出ACK,而Leader會(huì)等待該ACK消息;
  • Leader最后發(fā)送UPTODATE命令至Follower,這個(gè)命令的作用應(yīng)該是告訴Follower,我已經(jīng)收到了你的ACK,而Follower這邊收到該消息的時(shí)候說明一切與Leader同步的初始化工作都已經(jīng)完成,可以進(jìn)入正常的處理流程了,而Leader這邊發(fā)完該命令后也可以進(jìn)入正常的請(qǐng)求處理流程了。
  • 總結(jié)

    使用日志實(shí)現(xiàn)分布式系統(tǒng)一致性的方案中,日志代表了系統(tǒng)中發(fā)生的事件,而日志存在兩種狀態(tài)

    • 發(fā)起(Proposal)日志已經(jīng)被記錄在Leader/Follower的日志文件中,相當(dāng)于節(jié)點(diǎn)已經(jīng)記錄了該事件;
    • 提交(Commit):一旦事件被多數(shù)節(jié)點(diǎn)記錄,Leader節(jié)點(diǎn)便提交該日志,即處理事件。事件被處理完成后,Leader才會(huì)給予客戶端答復(fù),后續(xù),Leader節(jié)點(diǎn)同樣會(huì)將該Commit命令通知Follower節(jié)點(diǎn)。

    一旦日志被提交,那么在客戶端看來事件已經(jīng)被系統(tǒng)處理,那該事件產(chǎn)生的狀態(tài)就不能憑空消失,因此,在選主協(xié)議中最重要的兩點(diǎn)保證是:

    • 已經(jīng)被處理的消息不能丟
    • 被丟棄的消息不能再次出現(xiàn)

    已經(jīng)被處理的消息不能丟

    這一情況會(huì)出現(xiàn)在以下場(chǎng)景:當(dāng) leader 收到合法數(shù)量 follower 的 ACKs 后,就向各個(gè) follower 廣播 COMMIT 命令,同時(shí)也會(huì)在本地執(zhí)行 COMMIT 并向連接的客戶端返回「成功」。但是如果在各個(gè) follower 在收到 COMMIT 命令前 leader 就掛了,導(dǎo)致剩下的服務(wù)器并沒有執(zhí)行都這條消息。

    如圖,消息 1 的 COMMIT命令C1在 Server1(Leader)和 Server2(Follower)上執(zhí)行了,但是在Server3收到消息C1之前Server1便掛了,客戶端很可能已經(jīng)收到消息1已經(jīng)成功執(zhí)行的回復(fù),協(xié)議需要保證重新選主后,C1消息不會(huì)丟失。

    為了實(shí)現(xiàn)該目的,Zab選主時(shí)使用以下的策略:

    選擇擁有 proposal 最大值(即 zxid 最大) 的節(jié)點(diǎn)作為新的 Leader。

    由于所有提案被COMMIT 之前必須有大多數(shù)量的 Follower ACK,即大多數(shù)服務(wù)器已經(jīng)將該 proposal寫入日志文件。因此,新選出的Leader如果滿足是大多數(shù)節(jié)點(diǎn)中proposal最多的,它就必然存有所有被COMMIT消息的proposal。

    接下來,新Leader與Follower 建立先進(jìn)先出的隊(duì)列, 先將自身有而Follower缺失的proposal發(fā)送給 它,再將這些 proposal的COMMIT命令發(fā)送給 Follower,這便保證了所有的Follower都保存了所有的 proposal、所有的Follower 都處理了所有的消息。

    通過以上策略,能保證已經(jīng)被處理的消息不會(huì)丟

    被丟棄的消息不能再次出現(xiàn)

    這一情況會(huì)出現(xiàn)在以下場(chǎng)景:當(dāng)Leader 接收到消息請(qǐng)求生成 proposal后就掛了,其他Follower 并沒有收到此proposal,因此新選出的Leader中必然不含這條消息。 此時(shí),假如之前掛了的Leader 重新啟動(dòng)并注冊(cè)成了Follower,它要與新的Leader保持一致,就必須要?jiǎng)h除自己上舊的proposal。

    Zab 通過巧妙的設(shè)計(jì) zxid 來實(shí)現(xiàn)這一目的。一個(gè) zxid 是64位,高 32 是紀(jì)元(epoch)編號(hào),每經(jīng)過一次 Leader選舉產(chǎn)生一個(gè)新的Leader,其epoch 號(hào) +1。低 32 位是消息計(jì)數(shù)器,每接收到一條消息這個(gè)值 +1,新Leader 選舉后這個(gè)值重置為 0。

    這樣設(shè)計(jì)的目的是即使舊的Leader 掛了后重啟,它也不會(huì)被選舉為L(zhǎng)eader,因?yàn)榇藭r(shí)它的zxid 肯定小于當(dāng)前的新Leader。另外,當(dāng)舊的Leader 作為Follower提供服務(wù),新的Leader也會(huì)讓它將所有多余未被COMMIT的proposal清除。

    參考

    Zookeeper源碼分析-Zookeeper Leader選舉算法深入淺出Zookeeper之六 Leader/Follower初始化

    總結(jié)

    以上是生活随笔為你收集整理的ZAB协议选主过程详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。