我們知道選舉超時(shí)會(huì)發(fā)起選舉般贼,我們看下瘸恼,具體的流程是怎樣的?
發(fā)起人
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都會(huì)進(jìn)行選舉計(jì)時(shí)(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)
}
- 基本如前所述棘脐,就是選舉超時(shí)發(fā)生后斜筐,會(huì)重新發(fā)起選舉
- 有個(gè)重點(diǎn)是隨機(jī)的超時(shí)時(shí)間,為了避免大家同時(shí)超時(shí)蛀缝,又同時(shí)發(fā)起投票顷链,導(dǎo)致票數(shù)過不了半的問題,隨機(jī)超時(shí)是解決這個(gè)性價(jià)比最高的辦法屈梁。
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)
}
- 如果該節(jié)點(diǎn)還有配置變更的沒有寫入狀態(tài)機(jī)的話嗤练,那么他沒有資格發(fā)起選舉榛了。
- 如果配置了PreVote,發(fā)起選舉r.campaign(campaignPreElection)
- 如果沒有配置PreVote的話煞抬,發(fā)起選舉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霜大,主要是重置各個(gè)節(jié)點(diǎn)的進(jìn)度,然后先給自己投一票革答,自己的任期當(dāng)然要+1战坤,不然你要玩假的么?
這里注意的是發(fā)出的消息類型是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) }
如果之前是準(zhǔn)選舉途茫,那么清空投票機(jī),lead清零溪食,不做太多改變囊卜,
這里注意的是發(fā)出的消息類型是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) }
其次错沃,要給成員報(bào)告自己最后一位的(任期+index)栅组,給大家校驗(yàn)。讓對(duì)方判斷要不要投票給自己捎废。
如果是campaignTransfer笑窜,附在ctx里面一起發(fā)出去。
總結(jié)
MsgVote和MsgPreVote的部分見