EtcdRaft源码分析(心跳)

这篇我们看下心跳在整个Raft里面是怎么牵一发而动全身,真可谓是Raft的发条。

Client

type Node interface {
   // Tick increments the internal logical clock for the Node by a single tick. Election
   // timeouts and heartbeat timeouts are in units of ticks.
   Tick()
}

首先,整个心跳需要外部驱动。

选举

func (r *raft) tickElection() {
    r.electionElapsed++

    if r.promotable() && r.pastElectionTimeout() {
        r.electionElapsed = 0
        r.Step(pb.Message{From: r.id, Type: pb.MsgHup})
    }
}

心跳会触发选举超时啦,具体流程可查看EtcdRaft源码分析(选举超时)部分。这里不再详述。

心跳

func (r *raft) tickHeartbeat() {
   r.heartbeatElapsed++
   r.electionElapsed++

   if r.electionElapsed >= r.electionTimeout {
      r.electionElapsed = 0
      if r.checkQuorum {
         r.Step(pb.Message{From: r.id, Type: pb.MsgCheckQuorum})
      }
      // If current leader cannot transfer leadership in electionTimeout, it becomes leader again.
      if r.state == StateLeader && r.leadTransferee != None {
         r.abortLeaderTransfer()
      }
   }

   if r.state != StateLeader {
      return
   }

   if r.heartbeatElapsed >= r.heartbeatTimeout {
      r.heartbeatElapsed = 0
      r.Step(pb.Message{From: r.id, Type: pb.MsgBeat})
   }
}
  • 首先心跳只有Leader才能发出,是Leader保证权力稳固的最重要的手段。
  • 当然,心跳的触发的同时要累加心跳超时和选举超时
  • 一般来说一直发心跳的话,不会可能选举超时,因为选举超时会差心跳一个数量级。不管如何,有可能是网络的原因,导致心跳没有发出。
    • 假如选举超时,那么首先要重置超时时间。
    • 如果设置checkQuorum,那么去调查下当前成员的活跃度,如果活跃度不够,直接转没有leader的follower
    • 如果当前正在进行权力转移,但超过了一个选举周期,那么这次转移认为失败,取消。想象一下,一个成功的权力交接在选举周期内才可能成功,越早越好不是么?假如超过,那么你当选的几率不会比别人高。
  • 不管怎样,有可能健康度检查失败,总之现在不是leader了,那当然就不能继续发心跳了。返回。
  • 最重要的是,心跳计时器到了,现在要立即给成员发心跳MsgBeat了。让人民知道我的存在。

Leader

case pb.MsgBeat:
   r.bcastHeartbeat()
   return nil

前面说了只有Leader才能发心跳,收到MsgBeat后,会去扩散给成员心跳。

bcastHeartbeat

func (r *raft) sendHeartbeat(to uint64, ctx []byte) {
   commit := min(r.getProgress(to).Match, r.raftLog.committed)
   m := pb.Message{
      To:      to,
      Type:    pb.MsgHeartbeat,
      Commit:  commit,
      Context: ctx,
   }

   r.send(m)
}
  • 当然要算取一个commit,最不济是对方进度的Match位置。
  • 给对方发MsgHeartbeat

Candidate

case pb.MsgHeartbeat:
   r.becomeFollower(m.Term, m.From) // always m.Term == r.Term
   r.handleHeartbeat(m)
  • 首先Candidate收到心跳,说明已经选出了Leader,那么你候选人的身份就尴尬了。马上向对方称臣。然后开始处理心跳才是上策。

Follower

case pb.MsgHeartbeat:
   r.electionElapsed = 0
   r.lead = m.From
   r.handleHeartbeat(m)
  • 收到心跳,那么Follower的选举超时就要清零,因为Leader还健在,你需要表忠心,保证不会发起选举。

handleHeartbeat

func (r *raft) handleHeartbeat(m pb.Message) {
   r.raftLog.commitTo(m.Commit)
   r.send(pb.Message{To: m.From, Type: pb.MsgHeartbeatResp, Context: m.Context})
}
  • 心跳包中会包含Leader的committed的index,所以首先先更新自己的commit
  • 给Leader发回MsgHeartbeatResp

Leader

case pb.MsgHeartbeatResp:
   pr.RecentActive = true
   pr.resume()

   // free one slot for the full inflights window to allow progress.
   if pr.State == ProgressStateReplicate && pr.ins.full() {
      pr.ins.freeFirstOne()
   }
   if pr.Match < r.raftLog.lastIndex() {
      r.sendAppend(m.From)
   }

   if r.readOnly.option != ReadOnlySafe || len(m.Context) == 0 {
      return nil
   }

   ackCount := r.readOnly.recvAck(m)
   if ackCount < r.quorum() {
      return nil
   }

   rss := r.readOnly.advance(m)
   for _, rs := range rss {
      req := rs.req
      if req.From == None || req.From == r.id { // from local member
         r.readStates = append(r.readStates, ReadState{Index: rs.index, RequestCtx: req.Entries[0].Data})
      } else {
         r.send(pb.Message{To: req.From, Type: pb.MsgReadIndexResp, Index: rs.index, Entries: req.Entries})
      }
   }
  • 首先,收到心跳反馈,就说明对方是活跃的,更新本地Progress的RecentActive和pause
  • 如果对方进度状态是ProgressStateReplicate,且ins是满的,释放第一个slot
    • 这里讲讲ins,也就是inflights
    • add的时候有限制,假如已经满了,不止会返回,而且会panic,同步将终止退出。
    • 首先在Leader发起同步日志的时候会往ins里面add最新的index。然后在收到对方响应的时候,会取ins里面移除<=发来的index的slot。一般正常说来,当我Leader往ins里面add的多少,最终接受的时候也是那么多。对方会向我看齐。
    • inflights的作用是干嘛?
      • 一个好处是一个心跳周期内,不至于发得很频繁,有一定的节制
      • 等待心跳的时候释放一个slot,让Leader继续发
  • 如果对方的Match位要小于自己最后一位,那么给对方发日志复制的消息
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容

  • 前言 Raft协议是现在使用最广泛的分布式一致性协议,这篇文章的本意不是翻译它的协议内容(已经有大神做过了,中文版...
    空挡阅读 5,347评论 0 8
  • 最好先通读一遍 raft 论文,raft 论文写的很通俗、工程范、却又不失严谨。认真看完后,顺着论文的思路直接可以...
    陈半仙儿阅读 1,110评论 0 3
  • Leader 假定现在已经选出leader,开始要准备给其他人做日志同步了。首先你要成为一个真正的Leader,需...
    Pillar_Zhong阅读 803评论 0 51
  • 概念说明 leader: 如果candidate收大多数(n/2+1)节点的投票,就会转换成leader,lead...
    Damon_330b阅读 803评论 0 0
  • 总会有一段时间,会不断怀疑自己,会忍受不了当下...... 于是尝试做出改变,想要跳出固定的生活模式,努力推翻厌恶...
    太阳当空照1阅读 421评论 0 0