EtcdRaft源碼分析(選舉超時(shí))

我們知道選舉超時(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的部分見

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末登疗,一起剝皮案震驚了整個(gè)濱河市排截,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌辐益,老刑警劉巖断傲,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異智政,居然都是意外死亡认罩,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門续捂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)垦垂,“玉大人,你說我怎么就攤上這事牙瓢〗俎郑” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵矾克,是天一觀的道長(zhǎng)页慷。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么酒繁? 我笑而不...
    開封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任滓彰,我火速辦了婚禮,結(jié)果婚禮上州袒,老公的妹妹穿的比我還像新娘揭绑。我一直安慰自己,他們只是感情好郎哭,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開白布洗做。 她就那樣靜靜地躺著,像睡著了一般彰居。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上撰筷,一...
    開封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天陈惰,我揣著相機(jī)與錄音,去河邊找鬼毕籽。 笑死抬闯,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的关筒。 我是一名探鬼主播溶握,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蒸播!你這毒婦竟也來(lái)了睡榆?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤袍榆,失蹤者是張志新(化名)和其女友劉穎胀屿,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體包雀,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宿崭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了才写。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片葡兑。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖赞草,靈堂內(nèi)的尸體忽然破棺而出讹堤,到底是詐尸還是另有隱情,我是刑警寧澤房资,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布蜕劝,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏岖沛。R本人自食惡果不足惜暑始,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望婴削。 院中可真熱鬧廊镜,春花似錦、人聲如沸唉俗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)虫溜。三九已至雹姊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間衡楞,已是汗流浹背吱雏。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留瘾境,地道東北人歧杏。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像迷守,于是被迫代替她去往敵國(guó)和親犬绒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容