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

EtcdRaft源码分析(选举超时)

作者: 小蜗牛爬楼梯 | 来源:发表于2020-04-07 15:40 被阅读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的部分见

    作者:Pillar_Zhong
    链接:https://www.jianshu.com/p/8d581424264a
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    相关文章

      网友评论

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

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