品味Zookeeper之選舉及數(shù)據(jù)一致性

品味Zookeeper之選舉及數(shù)據(jù)一致性

本文思維導(dǎo)圖

image

前言

為了高可用和數(shù)據(jù)安全起見(jiàn),zk集群一般都是由幾個(gè)節(jié)點(diǎn)構(gòu)成(由n/2+1,投票機(jī)制決定,肯定是奇數(shù)個(gè)節(jié)點(diǎn))。多節(jié)點(diǎn)證明它們之間肯定會(huì)有數(shù)據(jù)的通信,同時(shí),為了能夠使zk集群對(duì)外是透明的,一個(gè)整體對(duì)外提供服務(wù),那么客戶端訪問(wèn)zk服務(wù)器的數(shù)據(jù)肯定是要數(shù)據(jù)同步,也即數(shù)據(jù)一致性。

zk集群是Leader/Follower模式來(lái)保證數(shù)據(jù)同步的。整個(gè)集群同一時(shí)刻只能有一個(gè)Leader,其他都是Follower或Observer。Leader是通過(guò)選舉選出來(lái)的,這里涉及到ZAB協(xié)議(原子消息廣播協(xié)議)。

1.ZAB協(xié)議

1.1 概念理解

為了更好理解下文,先說(shuō)ZAB協(xié)議,它是選舉過(guò)程和數(shù)據(jù)寫(xiě)入過(guò)程的基石。ZAB的核心是定義會(huì)改變zk服務(wù)器數(shù)據(jù)狀態(tài)的事務(wù)請(qǐng)求的處理方式。

ZAB的理解:所有事務(wù)請(qǐng)求是由一個(gè)全局唯一的服務(wù)器來(lái)協(xié)調(diào)處理,這個(gè)的服務(wù)器就是Leader服務(wù)器,
其它服務(wù)器都是Follower服務(wù)器或Observer服務(wù)器。Leader服務(wù)器負(fù)責(zé)將一個(gè)客戶端的請(qǐng)求轉(zhuǎn)換成那個(gè)一個(gè)事務(wù)Proposal?(提議),將該P(yáng)roposal分發(fā)給集群中所有的Follower服務(wù)器。然后Leader服務(wù)器需要等待所有Follower服務(wù)器的應(yīng)答,當(dāng)Leader服務(wù)器收到超過(guò)半數(shù)的Follower服務(wù)器進(jìn)行了明確的應(yīng)答后,Leader會(huì)再次向所有的Follower服務(wù)器分發(fā)Commit消息,要求其將前一個(gè)Proposal進(jìn)行提交。

注意事務(wù)提議這個(gè)詞,就類(lèi)似 人大代表大會(huì)提議 ,提議就代表會(huì)有應(yīng)答,之間有通信。因此在zk的ZAB協(xié)議為了可靠性和可用性,會(huì)有投票,應(yīng)答等操作來(lái)保證整個(gè)zk集群的正常運(yùn)行。

總的來(lái)說(shuō)就是,涉及到客戶端對(duì)zk集群數(shù)據(jù)改變的行為都先由Leader統(tǒng)一響應(yīng),然后再把請(qǐng)求轉(zhuǎn)換為事務(wù)轉(zhuǎn)發(fā)給其他所有的Follower,F(xiàn)ollower應(yīng)答并處理事務(wù),最后再反饋。如果客戶端只是讀請(qǐng)求,那么zk集群所有的節(jié)點(diǎn)都可以響應(yīng)這個(gè)請(qǐng)求。

1.2 ZAB協(xié)議三個(gè)階段

  • 1.發(fā)現(xiàn)(選舉Leader過(guò)程)
  • 2.同步(選出Leader后,F(xiàn)ollower和Observer需進(jìn)行數(shù)據(jù)同步)
  • 3.廣播(同步之后,集群對(duì)外工作響應(yīng)請(qǐng)求,并進(jìn)行消息廣播,實(shí)現(xiàn)數(shù)據(jù)在集群節(jié)點(diǎn)的副本存儲(chǔ))

下面會(huì)逐點(diǎn)分析,但是在這之前先來(lái)了解了解zookeeper服務(wù)器的知識(shí)吧。

2.Zookeeper服務(wù)器

2.1 zk服務(wù)器角色

  • Leader
  • 事務(wù)請(qǐng)求的唯一調(diào)度和處理者,保證集群事務(wù)處理的順序序性
  • 集群內(nèi)部各服務(wù)器的調(diào)度者
  • Follower
  • 處理客戶端非事務(wù)請(qǐng)求,轉(zhuǎn)發(fā)事務(wù)請(qǐng)求給Leader服務(wù)器
  • 參與事務(wù)請(qǐng)求Proposal的投票
  • 參與Leader的選舉投票
  • Observer
  • 處理客戶端非事務(wù)請(qǐng)求,轉(zhuǎn)發(fā)事務(wù)請(qǐng)求給Leader服務(wù)器
  • 不參加任何形式的投票,包括選舉和事務(wù)投票(超過(guò)半數(shù)確認(rèn))
  • Observer的存在是為了提高zk集群對(duì)外提供讀性能的能力

整個(gè)zk集群的角色作用如下圖:

image

2.2 zk服務(wù)器狀態(tài)

  • LOOKING
  • 尋找Leader狀態(tài)
  • 當(dāng)服務(wù)器處于這種狀態(tài)時(shí),表示當(dāng)前沒(méi)有Leader,需要進(jìn)入選舉流程
  • FOLLOWING
  • 從機(jī)狀態(tài),表明當(dāng)前服務(wù)器角色是Follower
  • OBSERVING
  • 觀察者狀態(tài),表明當(dāng)前服務(wù)器角色是Observer
  • LEADING
  • 領(lǐng)導(dǎo)者狀態(tài),表明當(dāng)前服務(wù)器角色是Leader
  • ServerState 類(lèi)維護(hù)服務(wù)器四種狀態(tài)。


    image

zk服務(wù)器的狀態(tài)是隨著機(jī)器的變化而變化的。比如Leader宕機(jī)了,服務(wù)器狀態(tài)就變?yōu)長(zhǎng)OOKING,通過(guò)選舉后,某機(jī)器成為L(zhǎng)eader,服務(wù)器狀態(tài)就轉(zhuǎn)換為L(zhǎng)EADING。其他情況類(lèi)似。

2.3 zk服務(wù)器通信

集群嘛,節(jié)點(diǎn)之間肯定是要通信的。zokeeper通信有兩個(gè)特點(diǎn):

  • 1.使用的通信協(xié)議是TCP協(xié)議。在集群中到底是怎么連接的呢?還記得在配置zookeeper時(shí)要?jiǎng)?chuàng)建一個(gè)data目錄并在其他創(chuàng)建一個(gè)myid文件并寫(xiě)入唯一的數(shù)字嗎?zk服務(wù)器的TCP連接方向就是依賴這個(gè)myid文件里面的數(shù)字大小排列。數(shù)小的向數(shù)大的發(fā)起TCP連接。比如有3個(gè)節(jié)點(diǎn),myid文件內(nèi)容分別為1,2,3。zk集群的tcp連接順序是1向2發(fā)起TCP連接,2向3發(fā)起TCP連接。如果有n個(gè)節(jié)點(diǎn),那么tcp連接順序也以此類(lèi)推。這樣整個(gè)zk集群就會(huì)連接起來(lái)。

  • 2.zk服務(wù)器是多端口的。例如配置如下:

      tickTime=2000
      dataDir=/home/liangjf/app/zookeeper/data
      dataLogDir=/home/liangjf/app/zookeeper/log
      clientPort=2181
      initLimit=5
      syncLimit=2
      server.1=192.168.1.1:2888:3888
      server.2=192.168.1.2:2888:3888
      server.3=192.168.1.3:2888:3888
    
  • 第1個(gè)端口是通信和數(shù)據(jù)同步端口,默認(rèn)是2888

  • 第2個(gè)端口是投票端口,默認(rèn)是3888

3.選舉機(jī)制

3.1 選舉算法

從zookeeper開(kāi)始發(fā)布以來(lái),選舉的算法也慢慢優(yōu)化?,F(xiàn)在為了可靠性和高可用,從3.4.0版本開(kāi)始zookeeper只支持基于TcpFastLeaderElection選舉協(xié)議。

  • LeaderElection
  • Udp協(xié)議
  • AuthFastLeaderElection
  • udp
  • FastLeaderElection
  • Udp
  • Tcp

FastLeaderElection選舉協(xié)議使用TCP實(shí)現(xiàn)Leader投票選舉算法。它使用了類(lèi)對(duì)象quorumcnxmanager管理連接。該算法是基于推送的,可以通過(guò)調(diào)節(jié)參數(shù)來(lái)改變選舉的過(guò)程。第一,finalizewait決定等到?jīng)Q定Leader的時(shí)間。這是Leader選舉算法的一部分。
final static int finalizeWait = 200;(選舉Leader過(guò)程的進(jìn)程時(shí)間)
final static int maxNotificationInterval = 60000;(通知檢查選中Leader的時(shí)間間隔)
final static int IGNOREVALUE = -1;
這里先不詳細(xì)分析,下面3.5 選舉算法源碼分析及舉栗子才分析。

3.2 何時(shí)觸發(fā)選舉

選舉Leader不是隨時(shí)選舉的,畢竟選舉有產(chǎn)生大量的通信,造成網(wǎng)絡(luò)IO的消耗。因此下面情況才會(huì)出現(xiàn)選舉:

  • 集群?jiǎn)?dòng)
  • 服務(wù)器處于尋找Leader狀態(tài)
  • 當(dāng)服務(wù)器處于LOOKING狀態(tài)時(shí),表示當(dāng)前沒(méi)有Leader,需要進(jìn)入選舉流程
  • 崩潰恢復(fù)
  • Leader宕機(jī)
  • 網(wǎng)絡(luò)原因?qū)е逻^(guò)半節(jié)點(diǎn)與Leader心跳中斷

3.3 如何成為L(zhǎng)eader

  • 數(shù)據(jù)新舊程度
  • 只有擁有最新數(shù)據(jù)的節(jié)點(diǎn)才能有機(jī)會(huì)成為L(zhǎng)eader
  • 通過(guò)zxid的大小來(lái)表示數(shù)據(jù)的新,zxid越大代表數(shù)據(jù)越新
  • myid
  • 集群?jiǎn)?dòng)時(shí),會(huì)在data目錄下配置myid文件,里面的數(shù)字代表當(dāng)前zk服務(wù)器節(jié)點(diǎn)的編號(hào)
  • 當(dāng)zk服務(wù)器節(jié)點(diǎn)數(shù)據(jù)一樣新時(shí), myid中數(shù)字越大的就會(huì)被選舉成ОLeader
  • 當(dāng)集群中已經(jīng)有Leader時(shí),新加入的節(jié)點(diǎn)不會(huì)影響原來(lái)的集群
  • 投票數(shù)量
  • 只有得到集群中多半的投票,才能成為L(zhǎng)eader
  • 多半即:n/2+1,其中n為集群中的節(jié)點(diǎn)數(shù)量

3.4 重要的zxid

由3.3知道zxid是判斷能否成為L(zhǎng)eader的條件之一,它代表服務(wù)器的數(shù)據(jù)版本的新舊程度。

zxid由兩部分構(gòu)成:主進(jìn)程周期epoch和事務(wù)單調(diào)遞增的計(jì)數(shù)器。zxid是一個(gè)64位的數(shù),高32位代表主進(jìn)程周期epoch,低32位代表事務(wù)單調(diào)遞增的計(jì)數(shù)器。

主進(jìn)程周期epoch也叫epoch,是選舉的輪次,每選舉一次就遞增1。事務(wù)單調(diào)遞增的計(jì)數(shù)器在每次選舉完成之后就會(huì)從0開(kāi)始。

如果是比較數(shù)據(jù)新舊的話,直接比較就可以了。因?yàn)槿绻侵鬟M(jìn)程周期越大,即高32位越大,那么低32位就不用再看了。如果主進(jìn)程周期一致,低32位越大,整個(gè)zxid就越大。所以直接比較整個(gè)64位就可以了,不必高32位于高32位對(duì)比,低32位與低32位比較。

3.5 選舉算法源碼分析及舉栗子

3.5.1 舉栗子

zookeeper選舉有兩種情況:

  • 1.集群首次啟動(dòng)
  • 2.集群在工作時(shí)Leader宕機(jī)

選主原則如下(在選舉時(shí),對(duì)比次序是從上往下)

  • 1.New epoch is higher
  • 主周期更大,代所有一切是最新,就成為leader
  • 2.New epoch is the same as current epoch, but new zxid is higher
  • 主周期一致就是在同一輪選票中,zxid越大就成為leader,因?yàn)閿?shù)據(jù)更新
  • 3.New epoch is the same as current epoch, new zxid is the same as current zxid, but server id is higher
  • 主周期和zxid一致,就看機(jī)器的id(myid),myid越大就成為leader

同時(shí),在選舉的時(shí)候是投票方式進(jìn)行的,除主進(jìn)程周期外,投票格式為(myid,zxid)。

第一種情況,比較容易理解,下面以3臺(tái)機(jī)器為例子。

  • 三個(gè)zk節(jié)點(diǎn)A,B,C,三者開(kāi)始都沒(méi)有數(shù)據(jù),即Zxid一致,對(duì)應(yīng)的myid為1,2,3。
  • A啟動(dòng)myid為1的節(jié)點(diǎn),zxid為0,此時(shí)只有一臺(tái)服務(wù)器無(wú)法選舉出Leader
  • B啟動(dòng)myid為2的節(jié)點(diǎn),zxid為0,B的zxid與A一樣,比較myid,B的myid為2比A為1大,B成Leader
  • C啟動(dòng)myid為3的節(jié)點(diǎn),因?yàn)橐呀?jīng)有Leader節(jié)點(diǎn),則C直接加入集群,承認(rèn)B是leader

第二種情況,已5臺(tái)機(jī)器為例子。

  • 五個(gè)節(jié)點(diǎn)A,B,C,D,E,B是Leader,其他是Follower,myid分別為1,2,3,4,5,zxid分別為3,4,5,6,6。運(yùn)行到某個(gè)時(shí)刻時(shí)A,B掉線或宕機(jī),此時(shí)剩下C D E。在同一輪選舉中,C,D,E分別投自己和交叉投票。
  • 第一次投票,都是投自己。
  • 投票情況為:C:(3,5) D:(4,6) E:(5,6)。
  • 同時(shí)也會(huì)收到其他機(jī)器的投票。
  • 投票情況為:C:(3,5)(4,6)(5,6),D:(4,6)(3,5)(5,6),E:(5,6)(4,6)(3,5)
  • 機(jī)器內(nèi)部會(huì)根據(jù)選主原則對(duì)比投票,變更投票,投票情況為:C:(3,5)(4,6)(5,6)【不變更】。 D:(4,6)(4,6)(5,6)【變更】。E:(5,6)(5,6)(5,6)【變更】
  • 統(tǒng)計(jì)票數(shù),C-1票,D-3票,E-5票。因此E成為L(zhǎng)eader。

接下來(lái)就是對(duì)新Leader節(jié)點(diǎn)的檢查,數(shù)據(jù)同步,廣播,對(duì)外提供服務(wù)。

3.5.1 選舉算法源碼分析

選舉算法的全部代碼在FastLeaderElection類(lèi)中。其他的lookForLeader函數(shù)是選舉Leader的入口函數(shù)。

//每一輪選舉就會(huì)增大一次邏輯時(shí)鐘,同時(shí)更新事務(wù)
synchronized(this){
    logicalclock++;
    updateProposal(getInitId(), getInitLastLoggedZxid(), getPeerEpoch());
}

//一直循環(huán)選舉直到找到leader,這里把打印和不相關(guān)的都刪除了,方便分析。

while ((self.getPeerState() == ServerState.LOOKING) &&
        (!stop)){

    //從通知隊(duì)列拉取一個(gè)投票通知
    Notification n = recvqueue.poll(notTimeout,
            TimeUnit.MILLISECONDS);

    if(n == null){
        //看是否選舉時(shí)通知發(fā)送/接收超時(shí)
        int tmpTimeOut = notTimeout*2;
        notTimeout = (tmpTimeOut < maxNotificationInterval?
                tmpTimeOut : maxNotificationInterval);
    }
    else if(self.getVotingView().containsKey(n.sid)) {
        switch (n.state) {
        case LOOKING://只有zk服務(wù)器狀態(tài)為L(zhǎng)OOKING時(shí)才會(huì)進(jìn)行選舉
            // If notification > current, replace and send messages out
            if (n.electionEpoch > logicalclock) {
                //如果選舉時(shí)的邏輯時(shí)鐘大于發(fā)送通知來(lái)源的機(jī)器的邏輯時(shí)鐘,就把對(duì)方的修改為自己的。
                logicalclock = n.electionEpoch;
                recvset.clear();
                //并統(tǒng)計(jì)票數(shù),如果能成為leader就更新事務(wù)
                if(totalOrderPredicate(n.leader, n.zxid, n.peerEpoch,
                        getInitId(), getInitLastLoggedZxid(), getPeerEpoch())) {
                    updateProposal(n.leader, n.zxid, n.peerEpoch);
                } else {
                    //否者更新事務(wù)為對(duì)方的投票信息
                    updateProposal(getInitId(),
                            getInitLastLoggedZxid(),
                            getPeerEpoch());
                }
                sendNotifications();
            } else if (n.electionEpoch < logicalclock) {
                //如果通知來(lái)演的機(jī)器的邏輯時(shí)鐘比本次我的選舉時(shí)鐘低,直接返回,什么都不做。因?yàn)閷?duì)方?jīng)]機(jī)會(huì)成為leader
                if(LOG.isDebugEnabled()){
                    LOG.debug("Notification election epoch is smaller than logicalclock. n.electionEpoch = 0x"
                            + Long.toHexString(n.electionEpoch)
                            + ", logicalclock=0x" + Long.toHexString(logicalclock));
                }
                break;
            } else if (totalOrderPredicate(n.leader, n.zxid, n.peerEpoch,
                    proposedLeader, proposedZxid, proposedEpoch)) {
                //如果Epoch一樣,就看zxid的比較。不過(guò)還是會(huì)更新事務(wù)和回傳通知
                updateProposal(n.leader, n.zxid, n.peerEpoch);
                sendNotifications();
            }

            //把所有接收到的投票信息都放到recvset集合
            recvset.put(n.sid, new Vote(n.leader, n.zxid, n.electionEpoch, n.peerEpoch));

            //統(tǒng)計(jì)誰(shuí)的投票超過(guò)半數(shù),就成為leader
            if (termPredicate(recvset,
                    new Vote(proposedLeader, proposedZxid,
                            logicalclock, proposedEpoch))) {

                //驗(yàn)證一下,被選舉的leader是否有變化,就是看符不符合
                while((n = recvqueue.poll(finalizeWait,
                        TimeUnit.MILLISECONDS)) != null){
                    if(totalOrderPredicate(n.leader, n.zxid, n.peerEpoch,
                            proposedLeader, proposedZxid, proposedEpoch)){
                        //符合就放進(jìn)recvqueue集合
                        recvqueue.put(n);
                        break;
                    }
                }

                //改變選舉為leader的機(jī)器的狀態(tài)為L(zhǎng)EADING
                if (n == null) {
                    self.setPeerState((proposedLeader == self.getId()) ?
                            ServerState.LEADING: learningState());

                    Vote endVote = new Vote(proposedLeader,
                            proposedZxid, proposedEpoch);
                    leaveInstance(endVote);
                    return endVote;
                }
            }
            break;
        case FOLLOWING:
        case LEADING:
            //在同一輪選舉中,判斷所有的通知,并確認(rèn)自己是leader
            if(n.electionEpoch == logicalclock){
                recvset.put(n.sid, new Vote(n.leader, n.zxid, n.electionEpoch, n.peerEpoch));
                if(termPredicate(recvset, new Vote(n.leader,
                                n.zxid, n.electionEpoch, n.peerEpoch, n.state))
                                && checkLeader(outofelection, n.leader, n.electionEpoch)) {
                    self.setPeerState((n.leader == self.getId()) ?
                            ServerState.LEADING: learningState());

                    Vote endVote = new Vote(n.leader, n.zxid, n.peerEpoch);
                    leaveInstance(endVote);
                    return endVote;
                }
            }
            //在對(duì)外提供服務(wù)前,先廣播一次自己是leader的消息給所有follower,讓大家認(rèn)同我為leader。
            outofelection.put(n.sid, new Vote(n.leader, n.zxid,
                    n.electionEpoch, n.peerEpoch, n.state));
            if (termPredicate(outofelection, new Vote(n.leader,
                    n.zxid, n.electionEpoch, n.peerEpoch, n.state))
                    && checkLeader(outofelection, n.leader, n.electionEpoch)) {
                synchronized(this){
                    logicalclock = n.electionEpoch;
                    self.setPeerState((n.leader == self.getId()) ?
                            ServerState.LEADING: learningState());
                }
                Vote endVote = new Vote(n.leader, n.zxid, n.peerEpoch);
                leaveInstance(endVote);
                return endVote;
            }
            break;
        }
    } 
}

比較重要的子函數(shù)有以下這些:

  • 1.totalOrderPredicate。(投票比較變更原則,選舉的核心)

protected boolean totalOrderPredicate(long newId, long newZxid, long newEpoch, long curId, long curZxid, long curEpoch) {
    LOG.debug("id: " + newId + ", proposed id: " + curId + ", zxid: 0x" +
            Long.toHexString(newZxid) + ", proposed zxid: 0x" + Long.toHexString(curZxid));
    if(self.getQuorumVerifier().getWeight(newId) == 0){
        return false;
    }
    //按照這樣的順序比較優(yōu)先:Epoch > Zxid > myid
    return ((newEpoch > curEpoch) || 
            ((newEpoch == curEpoch) &&
            ((newZxid > curZxid) || ((newZxid == curZxid) && (newId > curId)))));
}
  • 2.termPredicate。(最終的計(jì)算票數(shù)。先把投票放到集合中,然后再統(tǒng)計(jì)。集合能去重)

private boolean termPredicate(
        HashMap<Long, Vote> votes,
        Vote vote) {

    HashSet<Long> set = new HashSet<Long>();
    for (Map.Entry<Long,Vote> entry : votes.entrySet()) {
        if (vote.equals(entry.getValue())){
            set.add(entry.getKey());
        }
    }
    return self.getQuorumVerifier().containsQuorum(set);
}
  • 3.Messenger。(構(gòu)造Messenger的時(shí)候創(chuàng)建2條線程WorkerSender和WorkerReceiver用于整個(gè)選舉的集群投票通信)

Messenger(QuorumCnxManager manager) {
    this.ws = new WorkerSender(manager);
    Thread t = new Thread(this.ws,
            "WorkerSender[myid=" + self.getId() + "]");
    t.setDaemon(true);
    t.start();

    this.wr = new WorkerReceiver(manager);
    t = new Thread(this.wr,
            "WorkerReceiver[myid=" + self.getId() + "]");
    t.setDaemon(true);
    t.start();
}

其他細(xì)節(jié)不多說(shuō)了,主要是sendqueue和recvqueue隊(duì)列存放待發(fā)送投票通知和接收投票通知,WorkerSender和WorkerReceiver兩條線程用于投票的通信,QuorumCnxManager manager用于真正和其他機(jī)器的tcp連接維護(hù)管理,Messenger是整個(gè)投票通信的管理者。

3.數(shù)據(jù)同步機(jī)制

3.1 同步準(zhǔn)備

完成選舉之后,為了數(shù)據(jù)一致性,需要進(jìn)行數(shù)據(jù)同步流程。

3.1.1 Leader準(zhǔn)備
  • Leader告訴其它follower當(dāng)前最新數(shù)據(jù)是什么即zxid
  • Leader構(gòu)建一個(gè)NEWLEADER的包,包括當(dāng)前最大的zxid,發(fā)送給所有的follower或者Observer
  • Leader給每個(gè)follower創(chuàng)建一個(gè)線程LearnerHandler來(lái)負(fù)責(zé)處理每個(gè)follower的數(shù)據(jù)同步請(qǐng)求,同時(shí)主線程開(kāi)始阻塞,等到超過(guò)一半的follwer同步完成,同步過(guò)程才完成,leader才真正成為leader
3.1.2 Follower準(zhǔn)備
  • 選舉完成后,嘗試與leader建立同步連接,如果一段時(shí)間沒(méi)有連接上就報(bào)連接超時(shí),重新回到選舉狀態(tài)FOLLOWING
  • 向leader發(fā)送FOLLOWERINFO包,帶上follower自己最大的zxid

3.2 同步初始化

同步初始化涉及到三個(gè)東西:minCommittedLog、maxCommittedLog、zxid
– minCommittedLog:最小的事務(wù)日志id,即zxid沒(méi)有被快照存儲(chǔ)的日志文件的第一條,每次快照存儲(chǔ)
完,會(huì)重新生成一個(gè)事務(wù)日志文件
– maxCommittedLog:事務(wù)日志中最大的事務(wù),即zxid

4.數(shù)據(jù)同步場(chǎng)景

  • 直接差異化同步(DIFF同步)
  • 僅回滾同步TRUNC?,即刪除多余的事務(wù)日志,比如原來(lái)的Leader宕機(jī)后又重新加入,可能存在它自己寫(xiě)
    入提交但是別的節(jié)點(diǎn)還沒(méi)來(lái)得及提交
  • 先回滾再差異化同步(TRUNC+DIFF同步)
  • 全量同步(SNAP同步)

不同的數(shù)據(jù)同步算法適用不同的場(chǎng)景。

5.廣播流程

  • 集群選舉完成,并且完成數(shù)據(jù)同步后,開(kāi)始對(duì)外服務(wù),接收讀寫(xiě)請(qǐng)求
  • 當(dāng)leader接收到客戶端新的事務(wù)請(qǐng)求后,會(huì)生成對(duì)新的事務(wù)proposal,并根據(jù)zxid的順序向所有的
    follower分發(fā)事務(wù)proposal
  • 當(dāng)follower收到leader的proposal時(shí),根據(jù)接收的先后順序處理proposal
  • 當(dāng)Leader收到follower針對(duì)某個(gè)proposal過(guò)半的ack后,則發(fā)起事務(wù)提交,重新發(fā)起一個(gè)commit的
    proposal
  • Follower收到commit的proposal后,記錄事務(wù)提交,并把數(shù)據(jù)更新到內(nèi)存數(shù)據(jù)庫(kù)
  • 補(bǔ)充說(shuō)明
  • 由于只有過(guò)半的機(jī)器給出反饋,則可能存在某時(shí)刻某些節(jié)點(diǎn)數(shù)據(jù)不是最新的
  • 如果需要確定讀取到的數(shù)據(jù)是最新的,則可以在讀取之前,調(diào)用sync方法進(jìn)行數(shù)據(jù)同步

6.小結(jié)

在zookeeper中,除了watcher機(jī)制,會(huì)話管理,最重要的就是選舉了。它是zookeeper集群的核心,也是廣泛應(yīng)用在商業(yè)中的前提。洋洋灑灑一大篇,可能存在一些不足,后面更加深入理解再來(lái)補(bǔ)充吧。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一、前言 前面學(xué)習(xí)了Zookeeper服務(wù)端的相關(guān)細(xì)節(jié),其中對(duì)于集群?jiǎn)?dòng)而言,很重要的一部分就是Leader選舉,...
    阿斯蒂芬2閱讀 17,724評(píng)論 4 19
  • 【轉(zhuǎn)自】http://www.cnblogs.com/leesf456/p/6107600.html 一、前言 前...
    lxqfirst閱讀 895評(píng)論 0 0
  • 最近在學(xué)習(xí)ZooKeeper,一直想寫(xiě)篇相關(guān)博文記錄下學(xué)習(xí)內(nèi)容,礙于自己是個(gè)拖延癥重度患者總是停留在準(zhǔn)備階段,直到...
    Lexus90511閱讀 8,106評(píng)論 5 14
  • 本文將從系統(tǒng)模型、序列化與協(xié)議、客戶端工作原理、會(huì)話、服務(wù)端工作原理以及數(shù)據(jù)存儲(chǔ)等方面來(lái)揭示ZooKeeper的技...
    端木軒閱讀 3,905評(píng)論 0 42
  • 一個(gè)真正的寫(xiě)數(shù)據(jù)流程是怎么樣的?一個(gè)真正的讀數(shù)據(jù)流程是怎么樣的?一個(gè)真正的同步數(shù)據(jù)流程是怎么樣的?從哪里到哪里?什...
    時(shí)待吾閱讀 4,309評(píng)論 0 14

友情鏈接更多精彩內(nèi)容