在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是谁,那么先空着。