下面我来说说选leader算法。
首先创建一张给自己的选票,填上自己的myid,zxid和epoch(这些值在前面都已经捡算出来的)
如果electionType=0,UDP算法的选举,这个暂时不管。
我们主要讨论下3:FastLeaderElection
1.首先要开启选举监听端口
2.启动后初始状态为looking
private ServerState state = ServerState.LOOKING;
(readonlymode.enabled模式的zookeeper不考虑)
更新提议id为myid,提议zxid为自己最新的zxid,epoch也是自己最新的,logicalclock++
并将其广播出去
for (QuorumServer server : self.getVotingView().values()) {
long sid = server.id;
ToSend notmsg = new ToSend(ToSend.mType.notification,
proposedLeader,
proposedZxid,
logicalclock,
QuorumPeer.ServerState.LOOKING,
sid,
proposedEpoch);
广播出去后开始等待,如果等不到回复,则检查网络或重新广播
Notification n = recvqueue.poll(notTimeout,
TimeUnit.MILLISECONDS);
/*
* Sends more notifications if haven't received enough.
* Otherwise processes new notification.
*/
if(n == null){
if(manager.haveDelivered()){
sendNotifications();
} else {
manager.connectAll();
}
如果收到了返回,首先我们要看它是不是有资格投票,如果是来自observer的消息则直接过滤不看
2.1如果对方也是looking状态,则看他的logiclock,
①.如果他的logiclock大于自己的,则更新自己的logiclock并清除自己接受的所有选票(recvset,这是一个map,保留每个server最新的提议),然后开始双方开始pk,
谁赢谁输,全在一个方法totalOrderPredicate,这个很重要,这是fast paxos的选票核心算法,但是很简单,就是先比epoch,再比zxid,最后是myid。
return ((newEpoch > curEpoch) ||
((newEpoch == curEpoch) &&
((newZxid > curZxid) || ((newZxid == curZxid) && (newId > curId)))));
如果对方胜出,则自己更新为对方的提议,否则还是用自己的,然后再一次将结果广播
②.如果他的logiclock小于自己的,则认为对方消息是过时的,忽略掉。注意这里只是忽略掉,zookeeper什么都没做,因为对端会收到自己的广播消息,在他那边走①的操作。
③.如果他的logiclock等于自己的,则双方pk,取胜出者提议。这里跟①不同的地方是不用清除自己接受的选票,因为logiclock的状态,表示自己和对方是在同一步骤的
做完上述后,将对方的选票更新到recvset,然后统计该提议是否占n/2+1,具体参见(一)中的生效算法QuorumVerifier
protected boolean termPredicate(
HashMap<Long, Vote> votes,
Vote vote) {
HashSet<Long> set = new HashSet<Long>();
/*
* First make the views consistent. Sometimes peers will have
* different zxids for a server depending on timing.
*/
for (Map.Entry<Long,Vote> entry : votes.entrySet()) {
if (vote.equals(entry.getValue())){
set.add(entry.getKey());
}
}
return self.getQuorumVerifier().containsQuorum(set);
如果超过半数了,这时我们还不能急着下结论,防止被提议的leader后续有变,我们还需要遍历后面的接收消息,看看有没有更优的(pk胜利的),如果还有我们将取出的消息放回去,进入下次循环。
// Verify if there is any change in the proposed leader
while((n = recvqueue.poll(finalizeWait,
TimeUnit.MILLISECONDS)) != null){
if(totalOrderPredicate(n.leader, n.zxid, n.peerEpoch,
proposedLeader, proposedZxid, proposedEpoch)){
recvqueue.put(n);
break;
}
}
如果没有,我们则根据提议的投票设置自己为follower还是leader,并结束选举
self.setPeerState((proposedLeader == self.getId()) ?
ServerState.LEADING: learningState());
Vote endVote = new Vote(proposedLeader,
proposedZxid,
logicalclock,
proposedEpoch);
leaveInstance(endVote);
return endVote;
2.2对方是observer,则直接退出,没有选举
2.3对方是follower/leader,需要做的事如下:
如果收到一个投票,logiclock跟自己一样,并且他的投票获胜了,这时我们要注意检查他选举的leader是不是还活着或者已经不是leader了,如果是对的,我们需要根据这个投票,将自己设置为leader还是follower,结束投票
如果logiclock跟自己不一样,则当对方pk赢了后我们要更新为对方的提议,并设置自己为leader还是follower。
最后将这个最后的投票保留在CurrentVote中结束。
综上所述,paxos是一个很简单也很复杂的协议。他的简单在于投票的规则,epoch>zxid>myid。他的复杂在于每个server与其他的server的沟通交互。大家协同按照这个规则来办事,直到完成这个任务。总结一下算法,就是刚开始大家竞争,凭借实力当leader。当leader确定后,后来者无论实力如何都要被动接受后来者不可造次的结局。