美文网首页
EtcdRaft源码分析(选举超时)

EtcdRaft源码分析(选举超时)

作者: Pillar_Zhong | 来源:发表于2019-03-14 16:26 被阅读0次

    我们知道选举超时会发起选举,我们看下,具体的流程是怎样的?

    发起人

    func (r *raft) becomeFollower(term uint64, lead uint64) {
       r.step = stepFollower
       r.reset(term)
       r.tick = r.tickElection
       r.lead = lead
       r.state = StateFollower
       r.logger.Infof("%x became follower at term %d", r.id, r.Term)
    }
    
    func (r *raft) becomeCandidate() {
       // TODO(xiangli) remove the panic when the raft implementation is stable
       if r.state == StateLeader {
          panic("invalid transition [leader -> candidate]")
       }
       r.step = stepCandidate
       r.reset(r.Term + 1)
       r.tick = r.tickElection
       r.Vote = r.id
       r.state = StateCandidate
       r.logger.Infof("%x became candidate at term %d", r.id, r.Term)
    }
    
    func (r *raft) becomePreCandidate() {
       // TODO(xiangli) remove the panic when the raft implementation is stable
       if r.state == StateLeader {
          panic("invalid transition [leader -> pre-candidate]")
       }
       // Becoming a pre-candidate changes our step functions and state,
       // but doesn't change anything else. In particular it does not increase
       // r.Term or change r.Vote.
       r.step = stepCandidate
       r.votes = make(map[uint64]bool)
       r.tick = r.tickElection
       r.lead = None
       r.state = StatePreCandidate
       r.logger.Infof("%x became pre-candidate at term %d", r.id, r.Term)
    }
    

    我们看到Follwer,Candidate,PreCandidate都会进行选举计时(tickElection)。

    tickElection

    func (r *raft) tickElection() {
        r.electionElapsed++
    
        if r.promotable() && r.pastElectionTimeout() {
            r.electionElapsed = 0
            r.Step(pb.Message{From: r.id, Type: pb.MsgHup})
        }
    }
    
    func (r *raft) pastElectionTimeout() bool {
        return r.electionElapsed >= r.randomizedElectionTimeout
    }
    
    func (r *raft) resetRandomizedElectionTimeout() {
        r.randomizedElectionTimeout = r.electionTimeout + globalRand.Intn(r.electionTimeout)
    }
    
    • 基本如前所述,就是选举超时发生后,会重新发起选举
    • 有个重点是随机的超时时间,为了避免大家同时超时,又同时发起投票,导致票数过不了半的问题,随机超时是解决这个性价比最高的办法。

    Step

    case pb.MsgHup:
            if r.state != StateLeader {
                ents, err := r.raftLog.slice(r.raftLog.applied+1, r.raftLog.committed+1, noLimit)
                if err != nil {
                    r.logger.Panicf("unexpected error getting unapplied entries (%v)", err)
                }
                if n := numOfPendingConf(ents); n != 0 && r.raftLog.committed > r.raftLog.applied {
                    r.logger.Warningf("%x cannot campaign at term %d since there are still %d pending configuration changes to apply", r.id, r.Term, n)
                    return nil
                }
    
                r.logger.Infof("%x is starting a new election at term %d", r.id, r.Term)
                if r.preVote {
                    r.campaign(campaignPreElection)
                } else {
                    r.campaign(campaignElection)
                }
            } else {
                r.logger.Debugf("%x ignoring MsgHup because already leader", r.id)
            }
    
    • 如果该节点还有配置变更的没有写入状态机的话,那么他没有资格发起选举。
    • 如果配置了PreVote,发起选举r.campaign(campaignPreElection)
    • 如果没有配置PreVote的话,发起选举r.campaign(campaignElection)

    campaign

    func (r *raft) campaign(t CampaignType) {
        var term uint64
        var voteMsg pb.MessageType
        if t == campaignPreElection {
            r.becomePreCandidate()
            voteMsg = pb.MsgPreVote
            // PreVote RPCs are sent for the next term before we've incremented r.Term.
            term = r.Term + 1
        } else {
            r.becomeCandidate()
            voteMsg = pb.MsgVote
            term = r.Term
        }
        if r.quorum() == r.poll(r.id, voteRespMsgType(voteMsg), true) {
            // We won the election after voting for ourselves (which must mean that
            // this is a single-node cluster). Advance to the next state.
            if t == campaignPreElection {
                r.campaign(campaignElection)
            } else {
                r.becomeLeader()
            }
            return
        }
        for id := range r.prs {
            if id == r.id {
                continue
            }
            r.logger.Infof("%x [logterm: %d, index: %d] sent %s request to %x at term %d",
                r.id, r.raftLog.lastTerm(), r.raftLog.lastIndex(), voteMsg, id, r.Term)
    
            var ctx []byte
            if t == campaignTransfer {
                ctx = []byte(t)
            }
            r.send(pb.Message{Term: term, To: id, Type: voteMsg, Index: r.raftLog.lastIndex(), LogTerm: r.raftLog.lastTerm(), Context: ctx})
        }
    }
    
    • 首先成为Candidate,主要是重置各个节点的进度,然后先给自己投一票,自己的任期当然要+1,不然你要玩假的么?

    • 这里注意的是发出的消息类型是MsgVote,而且任期是term

      r.becomeCandidate()
      voteMsg = pb.MsgVote
      term = r.Term
      
      func (r *raft) becomeCandidate() {
         // TODO(xiangli) remove the panic when the raft implementation is stable
         if r.state == StateLeader {
            panic("invalid transition [leader -> candidate]")
         }
         r.step = stepCandidate
         r.reset(r.Term + 1)
         r.tick = r.tickElection
         r.Vote = r.id
         r.state = StateCandidate
         r.logger.Infof("%x became candidate at term %d", r.id, r.Term)
      }
      
    • 如果之前是准选举,那么清空投票机,lead清零,不做太多改变,

    • 这里注意的是发出的消息类型是MsgPreVote,而且任期是term+1

      r.becomePreCandidate()
      voteMsg = pb.MsgPreVote
      // PreVote RPCs are sent for the next term before we've incremented r.Term.
      term = r.Term + 1
      
      func (r *raft) becomePreCandidate() {
          // TODO(xiangli) remove the panic when the raft implementation is stable
          if r.state == StateLeader {
              panic("invalid transition [leader -> pre-candidate]")
          }
          // Becoming a pre-candidate changes our step functions and state,
          // but doesn't change anything else. In particular it does not increase
          // r.Term or change r.Vote.
          r.step = stepCandidate
          r.votes = make(map[uint64]bool)
          r.tick = r.tickElection
          r.lead = None
          r.state = StatePreCandidate
          r.logger.Infof("%x became pre-candidate at term %d", r.id, r.Term)
      }
      
    • 其次,要给成员报告自己最后一位的(任期+index),给大家校验。让对方判断要不要投票给自己。

    • 如果是campaignTransfer,附在ctx里面一起发出去。

    总结

    MsgVote和MsgPreVote的部分见

    相关文章

      网友评论

          本文标题:EtcdRaft源码分析(选举超时)

          本文链接:https://www.haomeiwen.com/subject/igzxmqtx.html