最近看了下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。
以上,只要有超过半数的机器存活,经过投票,收敛后,最终会完成投票。