zookeeper源碼 — 2、集羣啓動—leader選舉

上一篇介紹了zookeeper的單機啓動,集羣模式下啓動和單機啓動有類似的地方,可是也有各自的特色。集羣模式的配置方式和單機模式也是不同的,這一篇主要包含如下內容:java

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

  • 服務器組件啓動apache

  • leader選舉服務器

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

集羣模式會有多臺server,每臺server根據不一樣的角色會有不一樣的狀態,server狀態的定義以下網絡

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

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

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

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

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

對應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的選舉。

相關文章
相關標籤/搜索