3. SOFAJRaft源码分析— 是如何进行选举的?

在上一篇文章当中,我们讲解了NodeImpl在init方法里面会初始化话的动作,选举也是在这个方法里面进行的,这篇文章来从这个方法里详细讲一下选举的过程。

由于我这里介绍的是如何实现的,所以请大家先看一下原理:SOFAJRaft 选举机制剖析 | SOFAJRaft 实现原理

文章比较长,我也慢慢的写了半个月时间~

选举过程分析

我在这里只把有关选举的代码列举出来,其他的代码暂且忽略
NodeImpl#init

public boolean init(final NodeOptions opts) { .... // Init timers //设置投票计时器 this.voteTimer = new RepeatedTimer("JRaft-VoteTimer", this.options.getElectionTimeoutMs()) { @Override protected void onTrigger() { //处理投票超时 handleVoteTimeout(); } @Override protected int adjustTimeout(final int timeoutMs) { //在一定范围内返回一个随机的时间戳 return randomTimeout(timeoutMs); } }; //设置预投票计时器 //当leader在规定的一段时间内没有与 Follower 舰船进行通信时, // Follower 就可以认为leader已经不能正常担任旗舰的职责,则 Follower 可以去尝试接替leader的角色。 // 这段通信超时被称为 Election Timeout //候选者在发起投票之前,先发起预投票 this.electionTimer = new RepeatedTimer("JRaft-ElectionTimer", this.options.getElectionTimeoutMs()) { @Override protected void onTrigger() { handleElectionTimeout(); } @Override protected int adjustTimeout(final int timeoutMs) { //在一定范围内返回一个随机的时间戳 //为了避免同时发起选举而导致失败 return randomTimeout(timeoutMs); } }; //leader下台的计时器 //定时检查是否需要重新选举leader this.stepDownTimer = new RepeatedTimer("JRaft-StepDownTimer", this.options.getElectionTimeoutMs() >> 1) { @Override protected void onTrigger() { handleStepDownTimeout(); } }; .... if (!this.conf.isEmpty()) { //新启动的node需要重新选举 stepDown(this.currTerm, false, new Status()); } .... }

在这个init方法里面会初始化三个计时器是和选举有关的:

voteTimer:这个timer负责定期的检查,如果当前的state的状态是候选者(STATE_CANDIDATE),那么就会发起选举

electionTimer:在一定时间内如果leader没有与 Follower 进行通信时,Follower 就可以认为leader已经不能正常担任leader的职责,那么就会进行选举,在选举之前会先发起预投票,如果没有得到半数以上节点的反馈,则候选者就会识趣的放弃参选。所以这个timer负责预投票

stepDownTimer:定时检查是否需要重新选举leader,如果当前的leader没有获得超过半数的Follower响应,那么这个leader就应该下台然后重新选举。

RepeatedTimer的分析我已经写好了:2. SOFAJRaft源码分析—JRaft的定时任务调度器是怎么做的?

我们先跟着init方法的思路往下看,一般来说this.conf里面装的是整个集群的节点信息,是不会为空的,所以会调用stepDown,所以先从这个方法看起。

leader下台 private void stepDown(final long term, final boolean wakeupCandidate, final Status status) { LOG.debug("Node {} stepDown, term={}, newTerm={}, wakeupCandidate={}.", getNodeId(), this.currTerm, term, wakeupCandidate); //校验一下当前节点的状态是否有异常,或正在关闭 if (!this.state.isActive()) { return; } //如果是候选者,那么停止选举 if (this.state == State.STATE_CANDIDATE) { //调用voteTimer的stop方法 stopVoteTimer(); //如果当前状态是leader或TRANSFERRING } else if (this.state.compareTo(State.STATE_TRANSFERRING) <= 0) { //让启动的stepDownTimer停止运作 stopStepDownTimer(); //清空选票箱中的内容 this.ballotBox.clearPendingTasks(); // signal fsm leader stop immediately if (this.state == State.STATE_LEADER) { //发送leader下台的事件给其他Follower onLeaderStop(status); } } // reset leader_id //重置当前节点的leader resetLeaderId(PeerId.emptyPeer(), status); // soft state in memory this.state = State.STATE_FOLLOWER; //重置Configuration的上下文 this.confCtx.reset(); updateLastLeaderTimestamp(Utils.monotonicMs()); if (this.snapshotExecutor != null) { //停止当前的快照生成 this.snapshotExecutor.interruptDownloadingSnapshots(term); } //设置任期为大的那个 // meta state if (term > this.currTerm) { this.currTerm = term; this.votedId = PeerId.emptyPeer(); //重设元数据信息保存到文件中 this.metaStorage.setTermAndVotedFor(term, this.votedId); } if (wakeupCandidate) { this.wakingCandidate = this.replicatorGroup.stopAllAndFindTheNextCandidate(this.conf); if (this.wakingCandidate != null) { Replicator.sendTimeoutNowAndStop(this.wakingCandidate, this.options.getElectionTimeoutMs()); } } else { //把replicatorGroup里面的所有replicator标记为stop this.replicatorGroup.stopAll(); } //leader转移的时候会用到 if (this.stopTransferArg != null) { if (this.transferTimer != null) { this.transferTimer.cancel(true); } // There is at most one StopTransferTimer at the same term, it's safe to // mark stopTransferArg to NULL this.stopTransferArg = null; } //启动 this.electionTimer.start(); }

一个leader的下台需要做很多交接的工作:

如果当前的节点是个候选人(STATE_CANDIDATE),那么这个时候会让它暂时不要投票

如果当前的节点状态是(STATE_TRANSFERRING)表示正在转交leader或是leader(STATE_LEADER),那么就需要把当前节点的stepDownTimer这个定时器给关闭

如果当前是leader(STATE_LEADER),那么就需要告诉状态机leader下台了,可以在状态机中对下台的动作做处理

重置当前节点的leader,把当前节点的state状态设置为Follower,重置confCtx上下文

停止当前的快照生成,设置新的任期,让所有的复制节点停止工作

启动electionTimer

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zypgxg.html