EtcdRaft源码分析(选举投票)

在EtcdRaft源码分析(选举超时)的篇章里面讲到,当超时的时候,Leader外的成员会造反发起选举。我们接下来看下Raft里面选民是怎么投票的。

投票

Step

case m.Term > r.Term:
   if m.Type == pb.MsgVote || m.Type == pb.MsgPreVote {
      force := bytes.Equal(m.Context, []byte(campaignTransfer))
      inLease := r.checkQuorum && r.lead != None && r.electionElapsed < r.electionTimeout
      if !force && inLease {
         // If a server receives a RequestVote request within the minimum election timeout
         // of hearing from a current leader, it does not update its term or grant its vote
         r.logger.Infof("%x [logterm: %d, index: %d, vote: %x] ignored %s from %x [logterm: %d, index: %d] at term %d: lease is not expired (remaining ticks: %d)",
            r.id, r.raftLog.lastTerm(), r.raftLog.lastIndex(), r.Vote, m.Type, m.From, m.LogTerm, m.Index, r.Term, r.electionTimeout-r.electionElapsed)
         return nil
      }
   }
   switch {
   case m.Type == pb.MsgPreVote:
      // Never change our term in response to a PreVote
   case m.Type == pb.MsgPreVoteResp && !m.Reject:
      // We send pre-vote requests with a term in our future. If the
      // pre-vote is granted, we will increment our term when we get a
      // quorum. If it is not, the term comes from the node that
      // rejected our vote so we should become a follower at the new
      // term.
   default:
      r.logger.Infof("%x [term: %d] received a %s message with higher term from %x [term: %d]",
         r.id, r.Term, m.Type, m.From, m.Term)
      if m.Type == pb.MsgApp || m.Type == pb.MsgHeartbeat || m.Type == pb.MsgSnap {
         r.becomeFollower(m.Term, m.From)
      } else {
         r.becomeFollower(m.Term, None)
      }
   }
  • 如果候选人的任期比自己高,如果开启了checkQuorum就说明Leader在每个心跳的周期都会去检查成员的活跃度,而且现在Leader还在,且现在还没有选举超时。
    • 这里我理解,首先checkQuorum如果探测到网络不健康,Leader会被撸掉,现在还存在说明网络是健康的。其次,还没到选举的时候,这次选举我严重怀疑正当性。所以忽略。
  • 这里有个点要提下,如果任期比我高,并且类型是同步日志或快照或心跳的消息,那么当前不管什么身份,全部要臣服,认对方为Leader。说明这三种类型只有可能是Leader发出。
    • 其他类型可以不认对方,但是全部贬为庶民Follower。
    • MsgPreVote,MsgPreVoteResp,和消息被拒绝这几种情况,可以不处理。这个待分析
  • 所以在Raft里面,对方的任期比自己高,是很严重的事情。必须要立即行动。不然形势会越来越严峻。
case m.Term < r.Term:
  ...
   } else if m.Type == pb.MsgPreVote {
      // Before Pre-Vote enable, there may have candidate with higher term,
      // but less log. After update to Pre-Vote, the cluster may deadlock if
      // we drop messages with a lower term.
      r.logger.Infof("%x [logterm: %d, index: %d, vote: %x] rejected %s from %x [logterm: %d, index: %d] at term %d",
         r.id, r.raftLog.lastTerm(), r.raftLog.lastIndex(), r.Vote, m.Type, m.From, m.LogTerm, m.Index, r.Term)
      r.send(pb.Message{To: m.From, Term: r.Term, Type: pb.MsgPreVoteResp, Reject: true})
   } 
   ...
}
  • 这里得提下,如果是预选举的情况,如果对方还没有自己的任期高,那么你资格还没我老,我当然要拒绝你啊。给他投反对票。
case pb.MsgVote, pb.MsgPreVote:
   if r.isLearner {
      // TODO: learner may need to vote, in case of node down when confchange.
      r.logger.Infof("%x [logterm: %d, index: %d, vote: %x] ignored %s from %x [logterm: %d, index: %d] at term %d: learner can not vote",
         r.id, r.raftLog.lastTerm(), r.raftLog.lastIndex(), r.Vote, m.Type, m.From, m.LogTerm, m.Index, r.Term)
      return nil
   }
   // We can vote if this is a repeat of a vote we've already cast...
   canVote := r.Vote == m.From ||
      // ...we haven't voted and we don't think there's a leader yet in this term...
      (r.Vote == None && r.lead == None) ||
      // ...or this is a PreVote for a future term...
      (m.Type == pb.MsgPreVote && m.Term > r.Term)
   // ...and we believe the candidate is up to date.
   if canVote && r.raftLog.isUpToDate(m.Index, m.LogTerm) {
      r.logger.Infof("%x [logterm: %d, index: %d, vote: %x] cast %s for %x [logterm: %d, index: %d] at term %d",
         r.id, r.raftLog.lastTerm(), r.raftLog.lastIndex(), r.Vote, m.Type, m.From, m.LogTerm, m.Index, r.Term)
      r.send(pb.Message{To: m.From, Term: m.Term, Type: voteRespMsgType(m.Type)})
      if m.Type == pb.MsgVote {
         // Only record real votes.
         r.electionElapsed = 0
         r.Vote = m.From
      }
   } else {
      r.logger.Infof("%x [logterm: %d, index: %d, vote: %x] rejected %s from %x [logterm: %d, index: %d] at term %d",
         r.id, r.raftLog.lastTerm(), r.raftLog.lastIndex(), r.Vote, m.Type, m.From, m.LogTerm, m.Index, r.Term)
      r.send(pb.Message{To: m.From, Term: r.Term, Type: voteRespMsgType(m.Type), Reject: true})
   }
  • 如果是learner,那么没有资格投票,你先赶上进度再说把。

  • 验证是否能投票

    • 首先如果已经给对方投过票了,不介意再投
    • 如果还没有投票,且现在还没有选出Leader,当然要投
    • 如果是准选举,对方的任期比自己高,可以考虑投票
  • 如果可以投票,这里分两种情况,PreElection和Election

    • 如果是Pre,那么它发起选举的时候会把当前term+1,那么这个方法会返回true的话,说明自己的任期真的比对方低。
    • 如果不是Pre,那么它发起选举的时候就是当前term,那么这个方法返回true,说明对方比自己只多不少。
    • 给对方发消息,MsgVoteResp或MsgPreVoteResp
    • 如果是正式选举的话,选举计时开始,记下来我给他投了票
func (l *raftLog) isUpToDate(lasti, term uint64) bool {
  return term > l.lastTerm() || (term == l.lastTerm() && lasti >= l.lastIndex())
}
  • 如果不能投票,给对方投反对票,Reject: true

Candidate&PreCandidate

候选人收到各个成员发来的投票,

case myVoteRespType:
   gr := r.poll(m.From, m.Type, !m.Reject)
   r.logger.Infof("%x [quorum:%d] has received %d %s votes and %d vote rejections", r.id, r.quorum(), gr, m.Type, len(r.votes)-gr)
   switch r.quorum() {
   case gr:
      if r.state == StatePreCandidate {
         r.campaign(campaignElection)
      } else {
         r.becomeLeader()
         r.bcastAppend()
      }
   case len(r.votes) - gr:
      // pb.MsgPreVoteResp contains future term of pre-candidate
      // m.Term > r.Term; reuse r.Term
      r.becomeFollower(r.Term, None)
   }
  • 统计现在收到的同意票,计下票数
  • 如果超过半数
    • 如果之前是准选举,那么这次开始玩真的
    • 如果之前是正式选举,那么恭喜你,你成功当选
  • 如果反对票超过半数,那么对不起,你落选了,直接变为庶民。但是现在还不知道leader是谁,那么先空着。

你可能感兴趣的:(EtcdRaft源码分析(选举投票))