最近看了下zookeeper的源碼,先整理下leader選舉機制
先看幾個關鍵數據結構和函數
服務可能處於的狀態,從名字應該很好理解
public enum ServerState { LOOKING, FOLLOWING, LEADING, OBSERVING; }
選票參數,還有Notification,參數也都差不多
static public class ToSend {
long leader; //leader id
long zxid; //選票的zxid
long electionEpoch; //投票輪數
QuorumPeer.ServerState state; //狀態
long sid; //投票人id
long peerEpoch; //選票的epoch
}
選票的比較邏輯也很簡單,依次比較幾個關鍵字段
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. 發起投票:
首先投票給自己,然后給所有Acceptor;
2. 等待ack
先判斷自身狀態
1)如果自身狀態不是LOOKING:
說明已經有多數派通過選舉結果,直接將選舉結果通知給來源方;
2)如果自身狀態是LOOKING:
檢查投票者狀態:
2.1)如果選票是LOOKING發起的,說明當前正在選舉,需要收集選票,檢查選舉條件:
首先檢查選票輪數:
如果小於自身投票的輪數,說明收到的選票過期,忽略;
如果大於自身投票的輪數,說明自己已經out:
清空選票箱,根據優先級更新自己的選票,然后notify;
如果等於自身投票的輪數,投票有效:
根據優先級更新自己的選票,然后notify;
將選票投入選票箱;
檢查最新選票是否可以通過:
不滿足通過條件:
繼續等待新的選票:
滿足通過條件:
接收新選票,看能否收到優先級更高的選票:
如果有優先級高的選票,繼續循環;
否則投票結束,更新狀態。
2.2)如果選票是FOLLOWING、LEADING發起的,說明已有多數派通過選舉,此時只需確認是否滿足多數派:
檢查選票是否滿足多數派:
2.2.1)滿足選票終結。
2.2.2)繼續循環等待
代碼流程還算清晰,再來考慮下實際投票中可能情況,以上邏輯能否滿足:
1. 一台宕機重啟的機器加入已有環境
此時肯定有一個多數派接收選票時進入狀態1),這個多數派會將當前選舉結果返回,這些選票的處理流程都會進入2.2,當所有選票收到時,2.2.1滿足,選舉結束
2. 一台機器加入正在投票中的環境
如果當前機器的,如果所有server都會接受優先級最高的投票,投票會逐漸收斂,最高優先級最高的選票當選,選舉結束;
3. 當集群中多數機器宕機重啟
存活的服務發現不滿足多數派,改變狀態為LOOKING,投票輪數+1, 然后重新開始投票,此時會進入上面情況2。
以上,只要有超過半數的機器存活,經過投票,收斂后,最終會完成投票。