asp影视网站源码,电商网站界面规范,长沙高端网站建设公司,上海 网站建设 排名启动electionTimer#xff0c;进行leader选举。 一段时间没有leader和follower通信#xff0c;就会超时#xff0c;开始选举leader过程。有个超时时间#xff0c;如果到了这个时间#xff0c;就会触发一个回调函数。具体如下:
private void handleElectionTimeout() {boo…启动electionTimer进行leader选举。 一段时间没有leader和follower通信就会超时开始选举leader过程。有个超时时间如果到了这个时间就会触发一个回调函数。具体如下:
private void handleElectionTimeout() {boolean doUnlock true;this.writeLock.lock();try {if (this.state ! State.STATE_FOLLOWER) {return;}//如果当前选举没有超时则说明此轮选举有效if (isCurrentLeaderValid()) {return;}resetLeaderId(PeerId.emptyPeer(), new Status(RaftError.ERAFTTIMEDOUT, Lost connection from leader %s.,this.leaderId));doUnlock false;//预投票 (pre-vote) 环节//候选者在发起投票之前先发起预投票// 如果没有得到半数以上节点的反馈则候选者就会识趣的放弃参选preVote();} finally {if (doUnlock) {this.writeLock.unlock();}}
}在这个方法里首先会加上一个写锁然后进行校验最后先发起一个预投票。 校验的时候会校验当前的状态是不是follower校验leader和follower上次的通信时间是不是超过了ElectionTimeoutMs如果没有超过说明leader存活没必要发起选举如果通信超时那么会将leader置空然后调用预选举。(加写锁为什么。预投票过程会影响
NodeImpl#isCurrentLeaderValid
private boolean isCurrentLeaderValid() {return Utils.monotonicMs() - this.lastLeaderTimestamp this.options.getElectionTimeoutMs();
}复制代码用当前时间和上次leader通信时间相减如果小于ElectionTimeoutMs默认1s那么就没有超时说明leader有效 预投票过程
private void preVote() { … //返回最新的log实体类 final LogId lastLogId this.logManager.getLastLogId(true);
boolean doUnlock true;
this.writeLock.lock();
try {// pre_vote need defense ABA after unlockwriteLock//因为在上面没有重新加锁的间隙里可能会被别的线程改变了所以这里校验一下if (oldTerm ! this.currTerm) {LOG.warn(Node {} raise term {} when get lastLogId., getNodeId(), this.currTerm);return;}//初始化预投票投票箱this.prevVoteCtx.init(this.conf.getConf(), this.conf.isStable() ? null : this.conf.getOldConf());for (final PeerId peer : this.conf.listPeers()) {//如果遍历的节点是当前节点就跳过if (peer.equals(this.serverId)) {continue;}//失联的节点也跳过if (!this.rpcService.connect(peer.getEndpoint())) {LOG.warn(Node {} channel init failed, address{}., getNodeId(), peer.getEndpoint());continue;}//设置一个回调的类final OnPreVoteRpcDone done new OnPreVoteRpcDone(peer, this.currTerm);//向被遍历到的这个节点发送一个预投票的请求done.request RequestVoteRequest.newBuilder() //.setPreVote(true) // its a pre-vote request..setGroupId(this.groupId) //.setServerId(this.serverId.toString()) //.setPeerId(peer.toString()) //.setTerm(this.currTerm 1) // next term注意这里发送过去的任期会加一.setLastLogIndex(lastLogId.getIndex()) //.setLastLogTerm(lastLogId.getTerm()) //.build();this.rpcService.preVote(peer.getEndpoint(), done.request, done);}//自己也可以投给自己this.prevVoteCtx.grant(this.serverId);if (this.prevVoteCtx.isGranted()) {doUnlock false;electSelf();}
} finally {if (doUnlock) {this.writeLock.unlock();}
}}
首先会获取最新的log信息由LogId封装里面包含两部分一部分是这个日志的index和写入这个日志所对应当时节点的一个term任期 初始化预投票投票箱 遍历所有的集群节点 如果遍历的节点是当前节点就跳过如果遍历的节点因为宕机或者手动下线等原因连接不上也跳过 向遍历的节点发送一个RequestVoteRequest请求预投票给自己 最后因为自己也是集群节点的一员所以自己也投票给自己
在构造RequestVoteRequest的时候会将PreVote属性设置为true表示这次请求是预投票设置当前节点为ServerId传给对方的任期是当前节点的任期加一。最后在发送成功收到响应之后会回调OnPreVoteRpcDone的run方法。这个方法中如果收到正常的响应那么会调用handlePreVoteResponse方法处理响应。
这里做了3重校验我们分别来谈谈
第一重试校验了当前的状态如果不是FOLLOWER那么就不能发起选举。因为如果是leader节点那么它不会选举只能stepdown下台把自己变成FOLLOWER后重新选举如果是CANDIDATE那么只能进行由FOLLOWER发起的投票所以从功能上来说只能FOLLOWER发起选举。 从Raft 的设计上来说也只能由FOLLOWER来发起选举所以这里进行了校验。 第二重校验主要是校验发送请求时的任期和接受到响应时的任期还是不是一个如果不是那么说明已经不是上次那轮的选举了是一次失效的选举 第三重校验是校验响应返回的任期是不是大于当前的任期如果大于当前的任期那么重置当前的leader
校验完之后响应的节点会返回一个授权如果授权通过的话则调用Ballot的grant方法表示给当前的节点投一票
再看看预投票请求是怎么处理的 这个方法里面也是蛮有意思的写的很长但是逻辑很清楚。 首先调用isActive看一下当前节点是不是正常的节点不是正常节点要返回Error信息 将请求传过来的ServerId解析到candidateId实例中 校验当前的节点如果有leader并且leader有效的那么就直接break返回granted为false 如果当前的任期大于请求的任期那么调用checkReplicator检查自己是不是leader如果是leader那么将当前节点从failureReplicators移除重新加入到replicatorMap中。然后直接break 请求任期和当前任期相等的情况也要校验只是不用break 如果请求的日志比当前的最新的日志还要新那么返回granted为true代表授权成功
checkReplicator会从replicatorMap根据传入的peer实例校验一下是不是为空。因为replicatorMap里面存放了集群所有的节点。然后通过ReplicatorGroupImpl的commonOptions获取当前的Node实例如果当前的node实例是leader并且如果存在失败集合failureReplicators中的话就重新添加进replicatorMap中。
投票electself 其实都是和前面预选举的方法preVote重复度很高的。方法太长所以标了号从上面号码来一步步讲解 对当前的节点进行校验如果当前节点不在集群里面则不进行选举 因为是Follower发起的选举所以大概是因为要进行正式选举了把预选举定时器关掉 清空leader再进行选举注意这里会把votedId设置为当前节点代表自己参选 开始发起投票定时器因为可能投票失败需要循环发起投票voteTimer里面会根据当前的CANDIDATE状态调用electSelf进行选举 调用init方法初始化投票箱这里和prevVoteCtx是一样的 遍历所有节点然后向其他集群节点发送RequestVoteRequest请求这里也是和preVote一样的请求是被RequestVoteRequestProcessor处理器处理的。 如果有超过半数以上的节点投票选中那么就调用becomeLeader晋升为leader
预投票和投票的区别可以参见另一篇文章。
晋升leader 投票完毕之后如果收到的票数大于一半那么就会晋升为leader调用becomeLeader方法。 这个方法里面首先会停止选举定时器然后设置当前的状态为leader并设值任期然后遍历所有的节点将节点加入到复制集群中最后将stepDownTimer打开定时对leader进行校验是不是又半数以上的节点响应当前的leader。
一个leader的下台需要做很多交接的工作
如果当前的节点是个候选人STATE_CANDIDATE那么这个时候会让它暂时不要投票 如果当前的节点状态是STATE_TRANSFERRING表示正在转交leader或是leaderSTATE_LEADER那么就需要把当前节点的stepDownTimer这个定时器给关闭 如果当前是leaderSTATE_LEADER那么就需要告诉状态机leader下台了可以在状态机中对下台的动作做处理 重置当前节点的leader把当前节点的state状态设置为Follower重置confCtx上下文 停止当前的快照生成设置新的任期让所有的复制节点停止工作 启动electionTimer