前面我們介紹了Raft算法,接下來會分篇講述每一個部分,今天講述選舉的細節。
在講述選舉之前,先介紹下Raft算法基礎。
一、Raft基礎
1、節點角色
在Raft中,在任意時刻,服務器節點只能是以下3個角色之一:
Follower(跟隨者):系統啟動時默認的角色,一般來說不參與客戶端讀、寫請求,接受Leader發送過來的心跳追加日志,在Leader掛了之后轉變為Candidate;
Candidate(候選人):如果當前沒有Leader,Follower就轉變為這個角色,這個角色會向其它節點發起投票請求,如果多數節點同意投票,則晉升為Leader;
Leader(領導人):接受客戶端的讀、寫請求,協調整個日志的持久化和推進;
下面講節點角色時統一用英文描述。
2、節點角色狀態遷移圖

系統啟動時,大家都是Follower,然后啟動定時器,如果在指定時間沒有收到Leader的心跳,則將自己變成Candidate,然后向其它成員發起投票請求,如果收到過半以上成員的投票則Candidate晉升為Leader;
Leader發送心跳給其它成員時如果收到的響應中term比自己的大,則退化成Follower;
3、邏輯時鍾(term)
選舉過程有個term參數,這個參數就是邏輯時鍾,這是一個整數,全局遞增;Raft 把時間分割成任意長度的任期,用term來標識每一屆leader的任期,這樣可以保證在一個任期內只有一個Leader。
邏輯時鍾規則如下:
Candidate發起選舉時就將自己的term加1,然后發起投票請求;
收到投票請求的節點比較請求的term和自己的term,如果請求的term比自己的大,則更新自己的term;
這樣在即使每個節點的時間不一樣的情況下也可以推進邏輯時鍾;
4、狀態
| 狀態 | 所有服務器上持久存在的 |
|---|---|
| currentTerm | 服務器最后一次知道的任期號(初始化為 0,持續遞增) |
| votedFor | 在當前獲得選票的候選人的 Id |
| log[] | 日志條目集;每一個條目包含一個用戶狀態機執行的指令,和收到時的任期號 |
上面的狀態是所有節點都要保存的,並且要持久化的,即每次變更馬上要寫入磁盤。
| 狀態 | 所有服務器上經常變的 |
|---|---|
| commitIndex | 已知的最大的已經被提交的日志條目的索引值 |
| lastApplied | 最后被應用到狀態機的日志條目索引值(初始化為 0,持續遞增) |
上面的狀態是保存在內在中,每次重啟后都0開始,即不需要持久化到磁盤上。
| 狀態 | 在領導人里經常改變的 (選舉后重新初始化) |
|---|---|
| nextIndex[] | 對於每一個服務器,需要發送給他的下一個日志條目的索引值(初始化為領導人最后索引值加一) |
| matchIndex[] | 對於每一個服務器,已經復制給他的日志的最高索引值 |
上述只有在Leader節點才會需要保存,並且是也是保存在內存中,不需要持久化,重啟后從0開始。
二、領導人選舉
領導人選舉發生的條件為Follower沒收到Leader的心跳,具體場景一般如下:
1、系統啟動時
2、Leader掛了或網絡分區了
具體細節如下:
1、請求投票 RPC
由候選人發起
| 參數 | 解釋 |
|---|---|
| term | 候選人的任期號 |
| candidateId | 請求選票的候選人的 Id |
| lastLogIndex | 候選人的最后日志條目的索引值 |
| lastLogTerm | 候選人最后日志條目的任期號 |
返回值
| 返回值 | 解釋 |
|---|---|
| term | 當前任期號,以便於候選人去更新自己的任期號 |
| voteGranted | 候選人贏得了此張選票時為真 |
接收請求投票的節點響應規則如下:
- 如果term < currentTerm返回 false;
- 如果 votedFor 為空或者為 candidateId,並且候選人的日志至少和自己一樣新,那么就投票給他;
第1條規則好理解,第2條規則前面部分是為了保證在一個任期內每個節點只投1票,前面也說過這個信息是要持久化的;
候選人的日志至少和自己一樣新:這里說的就比較籠統了,這里的意思是要看下各自最后1條日志,即兩者的索引號和term都對的上,我們看一個實際的例子:

上面的例子從上往下假設分別為A、B、C、D、E節點,A當前為Leader,各節點日志索引如下:
A:8
B:5
C:8
D:2
E:7
如果這時候A掛了,如果D最先升級為Candidate,B、C、E收到請求后都不會為D投票,拿B來說,B發現D的最后一條日志索引為2,而自己的日志索引為8,因此拒絕B的請求。
關於選舉還有其它一些規則:
1、針對Follower
如果在超過選舉超時時間的情況之前都沒有收到Leader的心跳,或者是Candidate請求投票的,就自己變成Candidate;
2、針對Candidate
開始選舉后的動作如下:自增當前的任期號(currentTerm); 給自己投票; 重置選舉超時計時器;發送請求投票的 RPC 給其他所有服務器;
收到響應后的規則:如果接收到大多數服務器的選票,那么就變成Leader;如果接收到來自新的領導人的心跳信息,則轉變成Leader;如果選舉過程超時,再次發起一輪選舉;
3、針對Leader
一旦成為領導人:發送空的附加日志 RPC(心跳)給其他所有的服務器;在一定的空余時間之后不停的重復發送,以阻止跟隨者超時。

