上一篇介紹了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
作用:
- 參與leader選舉,可能被選為leader
- 接收處理讀請求
- 接收寫請求,轉發給leader,並參與投票決定寫操作是否提交
observer
為了支持zk集群可擴展性,如果直接增加follower的數量,會導致投票的性能下降。也就是防止參與投票的server太多,導致leader選舉收斂速度較慢,選舉所需時間過長。
observer和follower類似,但是不參與選舉和投票,
- 接收處理讀請求
- 接收寫請求,轉發給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 文件中讀取
- proposedLeader:一開始都是選舉自己,myid
-
發送自己的選票給其他參選者
-
接收其他參選者的選票
-
-
收到其他參選者的選票后會放入recvqueue,這個是阻塞隊列,從里面超時獲取
-
如果超時沒有獲取到選票vote則采用退避算法,下次使用更長的超時時間
-
校驗選票的有效性,並且當前機器處於looking狀態,開始判斷是否接受
-
-
如果收到的選票的electionEpoch大於當前機器選票的logicalclock
-
-
進行選票pk,收到的選票和本機初始選票pk,如果收到的選票勝出則更新本地的選票為收到的選票
-
-
pk的算法
-
- org.apache.zookeeper.server.quorum.FastLeaderElection#totalOrderPredicate
- 選取epoch較大的
- 如果epoch相等則取zxid較大的
- 如果zxid相等則取myid較大的
- org.apache.zookeeper.server.quorum.FastLeaderElection#totalOrderPredicate
-
-
-
如果本機初始選票勝出則更新為當前機器的選票
-
更新完選票之后重新發出自己的選票
-
-
-
-
-
- 如果n.electionEpoch < logicalclock.get()則丟棄選票,繼續准備接收其他選票
-
-
如果n.electionEpoch == logicalclock.get()並且收到的選票pk(pk算法totalOrderPredicate)之后勝出
-
- 更新本機選票並且,發送新的選票給其他參選者
-
執行到這里,說明收到的這個選票有效,將選票記錄下來,recvset
-
統計選票
-
- org.apache.zookeeper.server.quorum.FastLeaderElection#getVoteTracker
- 看看已經收到的投票中,和當前機器選票一致的票數
- org.apache.zookeeper.server.quorum.FastLeaderElection#getVoteTracker
-
判斷投票結果
-
-
org.apache.zookeeper.server.quorum.SyncedLearnerTracker#hasAllQuorums
-
根據具體的策略判斷
-
-
QuorumHierarchical
-
QuorumMaj,默認是這個
-
- 判斷投該票的主機數目是否占參與投票主機數的大部分,也就是大於1/2
-
-
-
如果本輪選舉成功
-
- 如果等finalizeWait時間后還沒有其他選票的時候,就認為當前選舉結束
- 設置當前主機狀態
- 退出本輪選舉
- 如果等finalizeWait時間后還沒有其他選票的時候,就認為當前選舉結束
-
-
-
-
-
-
選舉的整個流程為
總結
集群啟動過程其實就是在單機啟動的部分基礎上,增加了關於集群的一些組件,而且有leader的選舉。