Rift算法圖文解析

1. Raft 節點狀態

從拜占庭將軍的故事映射到分佈式系統上,每個將軍相當於一個分佈式網絡節點,每個節點有三種狀態:Follower,Candidate,Leader,狀態之間是互相轉換的,可以參考下圖,具體的後面說。

每個節點上都有一個倒計時器 (Election Timeout),時間隨機在 150ms 到 300ms 之間。有幾種情況會重設 Timeout:

  1. 收到選舉的請求
  2. 收到 Leader 的 Heartbeat (後面會講到)

在 Raft 運行過程中,最主要進行兩個活動:

  1. 選主 Leader Election
  2. 複製日誌 Log Replication

2. 選主 Leader Election

2.1 正常情況下選主

假設現在有如圖5個節點,5個節點一開始的狀態都是 Follower。

在一個節點倒計時結束 (Timeout) 後,這個節點的狀態變成 Candidate 開始選舉,它給其他幾個節點發送選舉請求 (RequestVote)

其他四個節點都返回成功,這個節點的狀態由 Candidate 變成了 Leader,並在每個一小段時間後,就給所有的 Follower 發送一個 Heartbeat 以保持所有節點的狀態,Follower 收到 Leader 的 Heartbeat 後重設 Timeout。

這是最簡單的選主情況,只要有超過一半的節點投支持票了,Candidate 纔會被選舉爲 Leader,5個節點的情況下,3個節點 (包括 Candidate 本身) 投了支持就行。

2.2 Leader 出故障情況下的選主

一開始已經有一個 Leader,所有節點正常運行。

Leader 出故障掛掉了,其他四個 Follower 將進行重新選主。

4個節點的選主過程和5個節點的類似,在選出一個新的 Leader 後,原來的 Leader 恢復了又重新加入了,這個時候怎麼處理?在 Raft 裏,第幾輪選舉是有記錄的,重新加入的 Leader 是第一輪選舉 (Term 1) 選出來的,而現在的 Leader 則是 Term 2,所有原來的 Leader 會自覺降級爲 Follower

2.3 多個 Candidate 情況下的選主

假設一開始有4個節點,都還是 Follower。

有兩個 Follower 同時 Timeout,都變成了 Candidate 開始選舉,分別給一個 Follower 發送了投票請求。

兩個 Follower 分別返回了ok,這時兩個 Candidate 都只有2票,要3票才能被選成 Leader。

兩個 Candidate 會分別給另外一個還沒有給自己投票的 Follower 發送投票請求。

但是因爲 Follower 在這一輪選舉中,都已經投完票了,所以都拒絕了他們的請求。所以在 Term 2 沒有 Leader 被選出來。

這時,兩個節點的狀態是 Candidate,兩個是 Follower,但是他們的倒計時器仍然在運行,最先 Timeout 的那個節點會進行發起新一輪 Term 3 的投票。

兩個 Follower 在 Term 3 還沒投過票,所以返回 OK,這時 Candidate 一共有三票,被選爲了 Leader。

如果 Leader Heartbeat 的時間晚於另外一個 Candidate timeout 的時間,另外一個 Candidate 仍然會發送選舉請求。

兩個 Follower 已經投完票了,拒絕了這個 Candidate 的投票請求。

Leader 進行 Heartbeat, Candidate 收到後狀態自動轉爲 Follower,完成選主。

以上是 Raft 最重要活動之一選主的介紹,以及在不同情況下如何進行選主。

3. 複製日誌 Log Replication

3.1 正常情況下複製日誌

Raft 在實際應用場景中的一致性更多的是體現在不同節點之間的數據一致性,客戶端發送請求到任何一個節點都能收到一致的返回,當一個節點出故障後,其他節點仍然能以已有的數據正常進行。在選主之後的複製日誌就是爲了達到這個目的。

一開始,Leader 和 兩個 Follower 都沒有任何數據。

客戶端發送請求給 Leader,儲存數據 “sally”,Leader 先將數據寫在本地日誌,這時候數據還是 Uncommitted (還沒最終確認,紅色表示)

Leader 給兩個 Follower 發送 AppendEntries 請求,數據在 Follower 上沒有衝突,則將數據暫時寫在本地日誌,Follower 的數據也還是 Uncommitted。

Follower 將數據寫到本地後,返回 OK。Leader 收到後成功返回,只要收到的成功的返回數量超過半數 (包含Leader),Leader 將數據 “sally” 的狀態改成 Committed。( 這個時候 Leader 就可以返回給客戶端了)

Leader 再次給 Follower 發送 AppendEntries 請求,收到請求後,Follower 將本地日誌裏 Uncommitted 數據改成 Committed。這樣就完成了一整個複製日誌的過程,三個節點的數據是一致的,

3.2 Network Partition 情況下進行復制日誌

在 Network Partition 的情況下,部分節點之間沒辦法互相通信,Raft 也能保證在這種情況下數據的一致性。

一開始有 5 個節點處於同一網絡狀態下。

Network Partition 將節點分成兩邊,一邊有兩個節點,一邊三個節點。

兩個節點這邊已經有 Leader 了,來自客戶端的數據 “bob” 通過 Leader 同步到 Follower。

因爲只有兩個節點,少於3個節點,所以 “bob” 的狀態仍是 Uncommitted。所以在這裏,服務器會返回錯誤給客戶端

另外一個 Partition 有三個節點,進行重新選主。客戶端數據 “tom” 發到新的 Leader,通過和上節網絡狀態下相似的過程,同步到另外兩個 Follower。

因爲這個 Partition 有3個節點,超過半數,所以數據 “tom” 都 Commit 了。

網絡狀態恢復,5個節點再次處於同一個網絡狀態下。但是這裏出現了數據衝突 “bob" 和 “tom"

三個節點的 Leader 廣播 AppendEntries

兩個節點 Partition 的 Leader 自動降級爲 Follower,因爲這個 Partition 的數據 “bob” 沒有 Commit,返回給客戶端的是錯誤,客戶端知道請求沒有成功,所以 Follower 在收到 AppendEntries 請求時,可以把 “bob“ 刪除,然後同步 ”tom”,通過這麼一個過程,就完成了在 Network Partition 情況下的複製日誌,保證了數據的一致性。

小總結

Raft 是能夠實現分佈式系統強一致性的算法,每個系統節點有三種狀態 Follower,Candidate,Leader。實現 Raft 算法兩個最重要的事是:選主和複製日誌

參考鏈接:
Raft 官網:https://raft.github.io/

Raft 原理動畫 (推薦看看):http://thesecretlivesofdata.com/raft/

Raft 算法解析圖片來源:http://www.infoq.com/cn/articles/coreos-analyse-etcd

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章