一、MsgHup消息
在raft协议中我们看到,Leader节点推动心跳计时器,而Follower节点会推动选举计时器。源码主要在etcd的github.com/etcd-io/etcd/tree/master/raft/node.go和raft.go中,raft结构体中有一个electionElapsed字段,该字段是选举计时器的指针,逻辑时钟每推进一次,该字段的值递增1,逻辑时钟是有上层模块来推进的。当electionElapsed选举计时器超时时,则该Follower节点会发送MsgHup消息。
1.选举计时器逻辑时钟的推动
选举计时器及超时时间在raft结构体中为以下两个字段:
electionElapsed int //选举计时器的指针,其单位是逻辑时钟的刻度,逻辑时钟每推进一次,该字段值就会增加1
randomizedElectionTimeout int //随机生成的选举计时器超时时间
选举计时器是tickElection()函数来推动的,Node节点每收到一次逻辑时钟推进,就会调用一次该函数来推动逻辑时钟,并判断逻辑时钟是否超时,如果超时则发送MsgHup类型的消息。
//推动选举计时器,如果超时则发送MsgHub消息
func (r *raft) tickElection() {
r.electionElapsed++
if r.promotable() && r.pastElectionTimeout() {
r.electionElapsed = 0
r.Step(pb.Message{From: r.id, Type: pb.MsgHup})
}
}
从以上方法我们看到,当选举计时器超时,节点会调用Step方法发送MsgHup消息。
2.MsgHup消息流程
从tickElection方法中可以看到,当选举计时器超时时,会将raft节点的electionElapsed心跳计时器置零,并且调用Step方法发送该MsgHup消息。Step方法处理各种消息类型的消息,我们这里只看对MsgHup消息的处理。
如果当前节点是Leader,则忽略MsgHup类型的消息
如果当前节点不是Leader,
获取raftLog中已提交但未应用的Entry记录
检测是否有未应用的EntryConfChange记录,如果有就放弃发起选举的机会
检测当前集群是否开启了PreVote模式,如果开启了则发起PreElection预选举,没有开启则发起Election选举
调用campaign()发起选举
func (r *raft) Step(m pb.Message) error {
// ...... 其他类型消息处理略
switch m.Type { //根据Message的Type进行分类处理
case pb.MsgHup: //这里针对MsgHup类型进行处理(Flower转成PreCandidate发送的消息)
if r.state != StateLeader { //只有非Leader状态的节点才会处理MsgHup消息
//获取raftLog中已提交但未应用的Entry记录
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)
}
//检测是否有未应用的EntryConfChange记录,如果有就放弃发起选举的机会