1. 程式人生 > >Zookeeper-Zookeeper leader選舉

Zookeeper-Zookeeper leader選舉

在上一篇文章中我們大致瀏覽了zookeeper的啟動過程,並且提到在Zookeeper的啟動過程中leader選舉是非常重要而且最複雜的一個環節。那麼什麼是leader選舉呢?zookeeper為什麼需要leader選舉呢?zookeeper的leader選舉的過程又是什麼樣子的?本文的目的就是解決這三個問題。

首先我們來看看什麼是leader選舉。其實這個很好理解,leader選舉就像總統選舉一樣,每人一票,獲得多數票的人就當選為總統了。在zookeeper叢集中也是一樣,每個節點都會投票,如果某個節點獲得超過半數以上的節點的投票,則該節點就是leader節點了。

國家選舉總統是為了選一個最高統帥,治理國家。那麼zookeeper叢集選舉的目的又是什麼呢?其實這個要清楚明白的解釋還是挺複雜的。我們可以簡單點想這個問題:我們有一個zookeeper叢集,有好幾個節點。每個節點都可以接收請求,處理請求。那麼,如果這個時候分別有兩個客戶端向兩個節點發起請求,請求的內容是修改同一個資料。比如客戶端c1,請求節點n1,請求是set a = 1; 而客戶端c2,請求節點n2,請求內容是set a = 2;

那麼最後a是等於1還是等於2呢? 這在一個分散式環境裡是很難確定的。解決這個問題有很多辦法,而zookeeper的辦法是,我們選一個總統出來,所有的這類決策都提交給總統一個人決策,那之前的問題不就沒有了麼。

那我們現在的問題就是怎麼來選擇這個總統呢? 在現實中,選擇總統是需要宣講拉選票的,那麼在zookeeper的世界裡這又如何處理呢?我們還是show code吧。

在QuorumPeer的startLeaderElection方法裡包含leader選舉的邏輯。Zookeeper預設提供了4種選舉方式,預設是第4種: FastLeaderElection。

我們先假設我們這是一個嶄新的叢集,嶄新的叢集的選舉和之前執行過一段時間的選舉是有稍許不同的,後面會提及。

節點狀態: 每個叢集中的節點都有一個狀態 LOOKING, FOLLOWING, LEADING, OBSERVING。都屬於這4種,每個節點啟動的時候都是LOOKING狀態,如果這個節點參與選舉但最後不是leader,則狀態是FOLLOWING,如果不參與選舉則是OBSERVING,leader的狀態是LEADING。

開始這個選舉演算法前,每個節點都會在zoo.cfg上指定的監聽埠啟動監聽(server.1=127.0.0.1:20881:20882),這裡的20882就是這裡用於選舉的埠。

在FastLeaderElection裡有一個Manager的內部類,這個類裡有啟動了兩個執行緒:WorkerReceiver, WorkerSender。為什麼說選舉這部分複雜呢,我覺得就是這些執行緒就像左右互搏一樣,非常難以理解。顧名思義,這兩個執行緒一個是處理從別的節點接收訊息的,一個是向外傳送訊息的。對於外面的邏輯接收和傳送的邏輯都是非同步的。

這裡配置好了,QuorumPeer的run方法就開始執行了,這裡實現的是一個簡單的狀態機。因為現在是LOOKING狀態,所以進入LOOKING的分支,呼叫選舉演算法開始選舉了:

setCurrentVote(makeLEStrategy().lookForLeader());

而在lookForLeader裡主要是幹什麼呢?首先我們會更新一下一個叫邏輯時鐘的東西,這也是在分散式演算法裡很重要的一個概念,但是在這裡先不介紹,可以參考後面的論文。然後決定我要投票給誰。不過zookeeper這裡的選舉真直白,每個節點都選自己(汗),選我,選我,選我...... 然後向其他節點廣播這個選舉資訊。這裡實際上並沒有真正的傳送出去,只是將選舉資訊放到由WorkerSender管理的一個佇列裡。

synchronized(this){
    //邏輯時鐘           
    logicalclock++;
    //getInitLastLoggedZxid(), getPeerEpoch()這裡先不關心是什麼,後面會討論
    updateProposal(getInitId(), getInitLastLoggedZxid(), getPeerEpoch());
}

//getInitId() 即是獲取選誰,id就是myid裡指定的那個數字,所以說一定要唯一
private long getInitId(){
        if(self.getQuorumVerifier().getVotingMembers().containsKey(self.getId()))       
            return self.getId();
        else return Long.MIN_VALUE;
}

//傳送選舉資訊,非同步傳送
sendNotifications();

現在我們去看看怎麼把投票資訊投遞出去。這個邏輯在WorkerSender裡,WorkerSender從sendqueue裡取出投票,然後交給QuorumCnxManager傳送。因為前面傳送投票資訊的時候是向叢集所有節點發送,所以當然也包括自己這個節點,所以QuorumCnxManager的傳送邏輯裡會判斷,如果這個要傳送的投票資訊是傳送給自己的,則不傳送了,直接進入接收佇列。

public void toSend(Long sid, ByteBuffer b) {
        if (self.getId() == sid) {
             b.position(0);
             addToRecvQueue(new Message(b.duplicate(), sid));
        } else {
             //傳送給別的節點,判斷之前是不是傳送過
             if (!queueSendMap.containsKey(sid)) {
                 //這個SEND_CAPACITY的大小是1,所以如果之前已經有一個還在等待發送,則會把之前的一個刪除掉,傳送新的
                 ArrayBlockingQueue<ByteBuffer> bq = new ArrayBlockingQueue<ByteBuffer>(SEND_CAPACITY);
                 queueSendMap.put(sid, bq);
                 addToSendQueue(bq, b);

             } else {
                 ArrayBlockingQueue<ByteBuffer> bq = queueSendMap.get(sid);
                 if(bq != null){
                     addToSendQueue(bq, b);
                 } else {
                     LOG.error("No queue for server " + sid);
                 }
             }
             //這裡是真正的傳送邏輯了
             connectOne(sid);
                
        }
    }

connectOne就是真正傳送了。在傳送之前會先把自己的id和選舉地址傳送過去。然後判斷要傳送節點的id是不是比自己的id大,如果大則不傳送了。如果要傳送又是啟動兩個執行緒:SendWorker,RecvWorker(這種一個程序內許多不同種類的執行緒,各自幹活的狀態真的很難理解)。傳送邏輯還算簡單,就是從剛才放到那個queueSendMap裡取出,然後傳送。並且傳送的時候將傳送出去的東西放到一個lastMessageSent的map裡,如果queueSendMap裡是空的,就傳送lastMessageSent裡的東西,確保對方一定收到了。

看完了SendWorker的邏輯,再來看看資料接收的邏輯吧。還記得前面提到的有個Listener在選舉埠上啟動了監聽麼,現在這裡應該接收到資料了。我們可以看到receiveConnection方法。在這裡,如果接收到的的資訊裡的id比自身的id小,則斷開連線,並嘗試傳送訊息給這個id對應的節點(當然,如果已經有SendWorker在往這個節點發送資料,則不用了)。

如果接收到的訊息的id比當前的大,則會有RecvWorker接收資料,RecvWorker會將接收到的資料放到recvQueue裡。

而FastLeaderElection的WorkerReceiver執行緒裡會不斷地從這個recvQueue裡讀取Message處理。在WorkerReceiver會處理一些協議上的事情,比如訊息格式等。除此之外還會看看接收到的訊息是不是來自投票成員。如果是投票成員,則會看看這個訊息裡的狀態,如果是LOOKING狀態並且當前的邏輯時鐘比投票訊息裡的邏輯時鐘要高,則會發個通知過去,告訴誰是leader。在這裡,剛剛啟動的嶄新叢集,所以邏輯時鐘基本上都是相同的,所以這裡還沒判斷出誰是leader。不過在這裡我們注意到如果當前節點的狀態是LOOKING的話,接收邏輯會將接收到的訊息放到FastLeaderElection的recvqueue裡。而在FastLeaderElection會從這個recvqueue裡讀取東西。

這裡就是選舉的主要邏輯了:totalOrderPredicate

protected boolean totalOrderPredicate(long newId, long newZxid, long newEpoch, long curId, long curZxid, long curEpoch) {return ((newEpoch > curEpoch) || 
                ((newEpoch == curEpoch) &&
                ((newZxid > curZxid) || ((newZxid == curZxid) && (newId > curId)))));
    }

1. 判斷訊息裡的epoch是不是比當前的大,如果大則訊息裡id對應的server我就承認它是leader

2. 如果epoch相等則判斷zxid,如果訊息裡的zxid比我的大我就承認它是leader

3. 如果前面兩個都相等那就比較一下server id吧,如果比我的大我就承認它是leader。

關於前面兩個東西暫時我們不去關心它,對於新啟動的叢集這兩者都是相等的。

那這樣看來server id的大小也是leader選舉的一環啊(有的人生下來註定就不平凡,這都是命啊)。

最後我們來看看,很多文章所介紹的,如果超過一半的人說它是leader,那它就是leader的邏輯吧

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

        HashSet<Long> set = new HashSet<Long>();
        //遍歷已經收到的投票集合,將等於當前投票的集合取出放到set中
        for (Map.Entry<Long,Vote> entry : votes.entrySet()) {
            if (self.getQuorumVerifier().getVotingMembers().containsKey(entry.getKey())
                    && vote.equals(entry.getValue())){
                set.add(entry.getKey());
            }
        }
        
        //統計set,也就是投某個id的票數是否超過一半
        return self.getQuorumVerifier().containsQuorum(set);
    }

    public boolean containsQuorum(Set<Long> ackSet) {
        return (ackSet.size() > half);
    }

最後一關:如果選的是自己,則將自己的狀態更新為LEADING,否則根據type,要麼是FOLLOWING,要麼是OBSERVING。

到這裡選舉就結束了。

這裡介紹的是一個新叢集啟動時候的選舉過程,啟動的時候就是根據zoo.cfg裡的配置,向各個節點廣播投票,一般都是選投自己。然後收到投票後就會進行進行判斷。如果某個節點收到的投票數超過一半,那麼它就是leader了。 

瞭解了這個過程,我們來看看另外一個問題:

一個叢集有3臺機器,掛了一臺後的影響是什麼?掛了兩臺呢? 

掛了一臺:掛了一臺後就是收不到其中一臺的投票,但是有兩臺可以參與投票,按照上面的邏輯,它們開始都投給自己,後來按照選舉的原則,兩個人都投票給其中一個,那麼就有一個節點獲得的票等於2,2 > (3/2)=1 的,超過了半數,這個時候是能選出leader的。

掛了兩臺: 掛了兩臺後,怎麼弄也只能獲得一張票, 1 不大於 (3/2)=1的,這樣就無法選出一個leader了。

在前面介紹時,為了簡單我假設的是這是一個嶄新的剛啟動的叢集,這樣的叢集與工作一段時間後的叢集有什麼不同呢?不同的就是epoch和zxid這兩個引數。在新啟動的叢集裡這兩個一般是相等的,而工作一段時間後這兩個引數有可能有的節點落後其他節點,至於是為什麼,這個還要在後面的儲存和處理額胡斷請求的文章裡介紹。

* 關於邏輯時鐘,我們的分散式大牛Leslie Lamport曾寫過一篇論文:Time, Clocks, and the Ordering of Events in a Distributed System

相關推薦

【分散式】ZookeeperLeader選舉-選舉過程介紹

【分散式】Zookeeper的Leader選舉-選舉過程介紹 選舉開始,伺服器會各自為自己投票,在投票完成後,會將投票資訊傳送給叢集中的所有伺服器(觀察者伺服器不參與選舉)。 選票由兩部分組成:伺服器唯一標識myid和事務編號zxid,即(myid,xzid)。 zxid越大說明資料越新,在選擇演算法中

ZooKeeperLeader選舉

        Leader選舉是ZooKeeper中最重要的技術之一,也是保證分散式資料一致性的關鍵所在。 1.leader選舉          Leader選舉主要分為伺服器啟動時期的Leader選舉和伺服器執行期間的Leader選舉。以下是兩種情況的選舉大致步驟。

ZookeeperLeader選舉機制示例

本文介紹下zookeeper中leader選舉機制的基本用法和關鍵知識點。 一、 選項設定 提到Leader選舉,先需要重點介紹下建立znode時的Flag選項。 ZOO_EPHEMERAL ZOO_EPHEMERAL,用來標記當建立這個znode的節點和Zookeepe

ZookeeperLeader選舉過程

Leader在叢集中是一個非常重要的角色,負責了整個事務的處理和排程,保證分散式資料一致性的關鍵所在。既然Leader在ZooKeeper叢集中這麼重要所以一定要保證叢集在任何時候都有且僅有一個Leader存在。 概念 Zookeeper Server三種角色:Leader,Follower,Observer

zookeeper curator學習(配合spring boot模擬leader選舉

round 一段時間 .cn cti -s col tid void sco 基礎知識:http://www.cnblogs.com/LiZhiW/p/4930486.html 項目路徑:https://gitee.com/zhangjunqing/spring-boo

Zookeeper詳解(七):Zookeeper集群啟動過程和Leader選舉

文件 信息 accep upm ron factory 通信 pan actor Zookeeper集群啟動過程預啟動統一由QuorumPeerMain作為啟動類讀取zoo.cfg配置文件創建並啟動歷史文件清理器DatadirCleanupManager判斷當前是集群模式還

Zookeeper 原始碼(五)Leader 選舉

Zookeeper 原始碼(五)Leader 選舉 前面學習了 Zookeeper 服務端的相關細節,其中對於叢集啟動而言,很重要的一部分就是 Leader 選舉,接著就開始深入學習 Leader 選舉。 一、選舉規則 Leader 選舉是保證分散式資料一致性的關鍵所在。當 Zookeeper 叢集中的

Zookeeper leader選舉

讓我們分析如何在ZooKeeper集合中選舉leader節點。考慮一個叢集中有N個節點。leader選舉的過程如下: 所有節點建立具有相同路徑 /app/leader_election/guid_ 的順序、臨時節點。 ZooKeeper集合將附加10位序列號到路徑,建

zookeeper的原理和使用(二)-leader選舉

一、前言   前面學習了Zookeeper服務端的相關細節,其中對於叢集啟動而言,很重要的一部分就是Leader選舉,接著就開始深入學習Leader選舉。 二、Leader選舉   2.1 Leader選舉概述   Leader選舉是保證分散式資料一致性的關鍵所在。當Zookeepe

zookeeper leader 選舉演算法

http://www.cnblogs.com/leesf456/p/6107600.html 一、前言   前面學習了Zookeeper服務端的相關細節,其中對於叢集啟動而言,很重要的一部分就是Leader選舉,接著就開始深入學習Leader選舉。 二、Leader選舉

zookeeper二之Leader選舉

轉載:https://blog.csdn.net/u010670689/article/details/78054945 竊以為,對於zookeeper這種東西,僅僅知道怎麼安裝是遠遠不夠的(廢話麼這不是,,,),至少要對其幾個典型的應用場景進行了解,才能比較全面的知道zk究竟能幹啥,怎麼玩兒,

Zookeeper學習總結(中)——Leader選舉

“深入一點,會更快樂。” ——題記 Leader選舉是ZooKeeper最重要的技術之一,也是保證分散式資料一致性的關鍵所在。 1 預備知識 1.1 術語解釋 SID:伺服器ID,唯一標識一臺zk中的伺服器,和myid的值一致。 ZXID:事務ID,標識伺服器狀態

跟著例項學習ZooKeeper的用法: Leader選舉

ZooKeeper官方給出了使用zookeeper的幾種用途。 Leader Election Barriers Queues Locks Two-phased Commit 其它應用如Name Service, Configuration, Group Membership 在實際使用

zookeeper 中master選舉leader選舉

開始學zookeeper 中,master選舉於leader 選舉2個概念比較模糊,談下自己的理解,如有不對地方請指出共同進步。 1.master選舉原理 有多個master,每次只能有一個master負責主要的工作,其他的master作為備份,同時對負責工作的maste

Zookeeper-Zookeeper leader選舉

在上一篇文章中我們大致瀏覽了zookeeper的啟動過程,並且提到在Zookeeper的啟動過程中leader選舉是非常重要而且最複雜的一個環節。那麼什麼是leader選舉呢?zookeeper為什麼需要leader選舉呢?zookeeper的leader選舉的過程又是什麼樣子的?本文的目的就是解決這三個問題

zookeeper leader選舉機制

最近看了下zookeeper的原始碼,先整理下leader選舉機制 先看幾個關鍵資料結構和函式 服務可能處於的狀態,從名字應該很好理解 public enum ServerState {   LOOKING, FOLLOWING, LEADING, OBSERVING; } 選票引數,還有Notifi

ZooKeeper Notes 23】Leader選舉-來自郵件列表

logical clocks是用來唯一標識一輪Leader選舉的。 - 次Leader掛了之後,叢集中的其他機器都會對logical clocks值做自增操作。 - 新一輪的Leader選舉開始或新一輪的投票開始了的時候,會對logical clocks值做自增操作。 在

ZooKeeper使用場景-Leader選舉

Leader選舉又稱為master選舉是zookeeper中最為經典的應用場景了。 在分散式環境中,相同的業務應用分佈在不同的機器上,有些業務邏輯(例如一些耗時的計算,網路I/O處理),往往只需要讓整個叢集中的某一臺機器進行執行,其餘機器可以共享這個結果,這樣可以大大減少

ZooKeeper系列之十:ZooKeeper的一致性保證及Leader選舉

1)一致性保證 Zookeeper 是一種高效能、可擴充套件的服務。 Zookeeper 的讀寫速度非常快,並且讀的速度要比寫的速度更快。另外,在進行讀操作的時候, ZooKeeper 依然能夠為舊的資料提供服務。這些都是由於 ZooKeepe 所提供的一致性保證,它具有如下特點: 順序一致性

Zookeeper -- leader選舉

        Zookeeper的核心是源自廣播,實現這個機制的協議是Zab協議。Zab協議有兩種模式,分別是恢復模式和廣播模式。當伺服器啟動或者領導者崩潰後,Zab就進入了恢復模式,當領導者被選舉出來,且大多數Server完成了和leader的狀態同步以後,恢復模式就結