zookeeper源碼 — 二、集群啟動—leader選舉


上一篇介紹了zookeeper的單機啟動,集群模式下啟動和單機啟動有相似的地方,但是也有各自的特點。集群模式的配置方式和單機模式也是不一樣的,這一篇主要包含以下內容:

  • 概念介紹:角色,服務器狀態

  • 服務器組件啟動

  • leader選舉

概念介紹:角色,服務器狀態

集群模式會有多台server,每台server根據不同的角色會有不同的狀態,server狀態的定義如下

public enum ServerState {
    LOOKING, FOLLOWING, LEADING, OBSERVING;
}

LOOKING:表示服務器處於選舉狀態,說明集群正在進行投票選舉,選出leader

FOLLOWING:表示服務器處於following狀態,表示當前server的角色是follower

LEADING:表示服務器處於leading狀態,當前server角色是leader

OBSERVING:表示服務器處於OBSERVING狀態,當前server角色是OBSERVER

對應server的角色有:

leader

投票選出的leader,可以處理讀寫請求。處理寫請求的時候收集各個參與投票者的選票,來決出投票結果

follower

作用:

  1. 參與leader選舉,可能被選為leader
  2. 接收處理讀請求
  3. 接收寫請求,轉發給leader,並參與投票決定寫操作是否提交

observer

為了支持zk集群可擴展性,如果直接增加follower的數量,會導致投票的性能下降。也就是防止參與投票的server太多,導致leader選舉收斂速度較慢,選舉所需時間過長。

observer和follower類似,但是不參與選舉和投票,

  1. 接收處理讀請求
  2. 接收寫請求,轉發給leader,但是不參與投票,接收leader的投票結果,同步數據

這樣在支持集群可擴展性的同時又不會影響投票的性能

服務器組件啟動

集群模式下服務器啟動的組件一部分和單機模式下類似,只是啟動的流程和時機有所差別

  • FileTxnSnapLog
  • NIOServerCnxnFactory
  • Jetty

也是會啟動上面三個組件,但是因為集群模式還有其他組件需要啟動,所以具體啟動的邏輯不太一樣。

除了上面這些組件外,集群模式下還有一些用來支撐集群模式的組件

  • QuorumPeer:用來啟動各個組件,是選舉過程的mainloop,在loop中判斷當前server狀態來決定做不同的處理
  • FastLeaderElection:默認選舉算法
  • QuorumCnxManager:選舉過程中的網絡通信組件

QuorumPeer

解除出來的QuorumPeerConfig配置都設置到QuorumPeer對應的屬性中,主線程啟動完QuorumPeer后,調用該線程的join方法等待該線程退出。

QuorumCnxManager

負責各個server之間的通信,維護了和各個server之間的連接,下面的線程負責與其他server建立連接

org.apache.zookeeper.server.quorum.QuorumCnxManager.Listener

還維護了與其他每個server連接對應的發送隊列,SendWorker線程負責發送packet給其他server

final ConcurrentHashMap<Long, ArrayBlockingQueue<ByteBuffer>> queueSendMap;

這個map的key是建立網絡連接的server的myid,value是對應的發送隊列。

還有接收隊列,RecvWorker是用來接收其他server發來的Message的線程,將收到的Message放入隊列中

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

leader選舉

選舉入口在下面的方法中

org.apache.zookeeper.server.quorum.FastLeaderElection#lookForLeader

判斷投票結果的策略

上面這個是其中的一種選舉算法,選舉過程中,各個server收到投票后需要進行投票結果抉擇,判斷投票結果的策略有兩種

// 按照分組權重
org.apache.zookeeper.server.quorum.flexible.QuorumHierarchical

// 簡單按照是否是大多數,超過參與投票數的一半
org.apache.zookeeper.server.quorum.flexible.QuorumMaj

選票的網絡傳輸

zookeeper中選舉使用的端口和正常處理client請求的端口是不一樣的,而且由於投票的數據和處理請求的數據不一樣,數據傳輸的方法也不一樣。選舉使用的網絡傳輸相關的類和數據結構如下

選舉過程

  • 各自初始化選票

    • proposedLeader:一開始都是選舉自己,myid
      • proposedZxid:最后一次處理成功的事務的zxid
      • proposedEpoch:上一次選舉成功的leader的epoch,從currentEpoch 文件中讀取
  • 發送自己的選票給其他參選者

  • 接收其他參選者的選票

    • 收到其他參選者的選票后會放入recvqueue,這個是阻塞隊列,從里面超時獲取

      • 如果超時沒有獲取到選票vote則采用退避算法,下次使用更長的超時時間

      • 校驗選票的有效性,並且當前機器處於looking狀態,開始判斷是否接受

        • 如果收到的選票的electionEpoch大於當前機器選票的logicalclock

            • 進行選票pk,收到的選票和本機初始選票pk,如果收到的選票勝出則更新本地的選票為收到的選票

                • pk的算法

                    • org.apache.zookeeper.server.quorum.FastLeaderElection#totalOrderPredicate
                      • 選取epoch較大的
                      • 如果epoch相等則取zxid較大的
                      • 如果zxid相等則取myid較大的
              • 如果本機初始選票勝出則更新為當前機器的選票

              • 更新完選票之后重新發出自己的選票

        • 如果n.electionEpoch < logicalclock.get()則丟棄選票,繼續准備接收其他選票
        • 如果n.electionEpoch == logicalclock.get()並且收到的選票pk(pk算法totalOrderPredicate)之后勝出

            • 更新本機選票並且,發送新的選票給其他參選者
          • 執行到這里,說明收到的這個選票有效,將選票記錄下來,recvset

          • 統計選票

            • org.apache.zookeeper.server.quorum.FastLeaderElection#getVoteTracker
              • 看看已經收到的投票中,和當前機器選票一致的票數
          • 判斷投票結果

            • org.apache.zookeeper.server.quorum.SyncedLearnerTracker#hasAllQuorums

              • 根據具體的策略判斷

                • QuorumHierarchical

                  • QuorumMaj,默認是這個

                    • 判斷投該票的主機數目是否占參與投票主機數的大部分,也就是大於1/2
              • 如果本輪選舉成功

                • 如果等finalizeWait時間后還沒有其他選票的時候,就認為當前選舉結束
                  • 設置當前主機狀態
                  • 退出本輪選舉

選舉的整個流程為

總結

集群啟動過程其實就是在單機啟動的部分基礎上,增加了關於集群的一些組件,而且有leader的選舉。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM