Raft協議介紹(翻譯)

Raft協議介紹(翻譯)

原文鏈接

原文作者:Shubheksha
翻譯:@vision9527
時間:2019年11月

本文討論的的raft一致性算法來自於論文In Search of An Understandable Consensus Algorithm.所有的引用都出自這篇文章.

Raft

Raft 是一個分佈式一致性算法,它的設計很容易被理解。Raft算法解決了即使在出現故障時,多個服務器同意同一個共享狀態的問題。共享狀態通常是由複製日誌數據結構來保證的。只要大多數服務器是有效的,系統就是有效和可運行的。

Raft通過在集羣中選舉leader來工作和運行。leader負責接受客戶端的請求和管理髮往集羣其他服務器的複製日誌。數據流只有一個方向:從leader到其它服務器。

Raft分解一致性問題爲三個子問題:

  • leader的選舉:leader需要被選舉以防存在的leader失效
  • 日誌的複製:leader需要保證所有集羣所有服務器的日誌與它自己的日誌同步
  • 安全性:如果服務器已經在某個索引提交了日誌條目,那麼沒有任何其它服務器的日誌條目可以在此索引修改

Raft一直保持這些狀態都是存在
以上是raft五個比較重要的概念

  • 選舉安全:在一個任期內只有一個leader
  • leader只能追加:leader不能覆蓋或者刪除日誌,僅僅能追加日誌
  • 日誌匹配:如果包含同一個entry的兩個日誌有相同索引和任期,那麼它們之前的的日誌都是相同的(譯者注:此條件是保持日誌一致的最根本原因)。
  • leader完備性:如果一個log在某個任期內提交了,那麼這個log會出現在所有更高任期的leader的log中。
  • 狀態機安全:如果一個服務器已經在給定的索引值位置的日誌條目應用到狀態機中,那麼其他任何的服務器在這個索引位置不會提交一個不同的日誌。

Raft 基礎

每個集羣中的節點都在此三種狀態之一:leader,follower,candidate。
節點狀態的改變如下:
節點狀態的改變
在通常的狀態下,集羣只有一個節點是leader,其餘節點都是follower。follower是被動的:它們不會主動發起請求,但是會迴應來自leader和candidate節點的請求。leader會處理所有來自客戶端的請求(如果一個請求發送到了follower,那麼那個follower會重定向到leader)。第三種狀態,candidate,只是用於選舉一個新的leader。

Raft將時間劃分爲任意長度的任期(term),每個長度以選舉開始。如果某個candidate贏得了選舉,那麼它會是此term的leader直到此term結束。如果投票出現分歧,那麼此term會以無leader結束。

任期的期數單調增加。每個節點都會存儲當前的任期期數並且在每次交流中都會交換。

如果某個節點的當前任期小於其它節點,那麼它會更新它的任期到最大值。如果某個candidate或者leader發現它自己的任期過期了,它們會立刻還原成follower狀態。如果某個節點收到錯誤的任期數,它會拒絕此次請求。

Raft使用兩個rpc來執行基本操作:

  • RequestVotes(投票請求),在選舉期間由candidate發送
  • AppendEntries(增加條目),由leader用於複製日誌和心跳檢測(一個檢查節點是否存活的信號,不會有任何的日誌條目)

Leader 選舉

leader週期的發送心跳包給follower維持它的權威(譯者注:就是讓別的節點認可它是leader的意思)。leader選舉會在follower等候來自leader的心跳包超時過後被處罰。此時follower會變成candidate狀態並且增加它的任期期數。在它收到投票後,它也會與其它節點並行的發出投票。因此會有三種結果產生:

  • candidate收到來自集羣中大多數的投票然後成爲leader。接着它發送心跳包給其餘節點建立權威。
  • 如果其它的candidate收到AppendEntries RPC,它們會檢查任期數。如果任期數大於它們自己當前的任期數,那麼它們會接受並承認此leader的狀態同時退回成follower的狀態。如果任期數小於它們自己當前的任期數,那麼它們會拒絕此rpc的請求並且維持candidate的狀態。
  • candidate要麼退回follower要麼成爲leader。如果在同一時間有多個節點成爲了candidate,投票可能會由於沒有明顯的多數情況下出現分歧。在這種情況下新的選舉會在某個candidate過期後重新進行。

Raft使用隨機的選舉超時時間(election timeout)來確保投票分歧是少數的,並且分歧的情況也是可以很快被解決的。爲了預防第一次投票(譯者注:服務啓動)出現分歧,election timeout會從固定間隔時間(例如150-300ms)中隨機選擇。因此在大多數情況只有一個節點會election timeout(目的就是儘量在一個時刻只有一個節點會變成candidate)。這樣此candidate節點就會在其它節點election timeout之前贏得選舉併發送心跳包給其它節點。同樣的機制也用於處理投票分歧。每個candidate會在選舉開始時重新獲得隨機的election timeout,並且在開始下一輪的選舉之前等待此超時時間。這樣就在新的選舉時減少了另一種投票分歧的可能行。

日誌複製

客戶機請求現在被假定爲只寫。每個請求都包含被所有節點的複製狀態機執行的命令。當leader接受到來自客戶端的請求,它將會增加請求到log作爲新的entry。每個包含在log中的entry有以下信息:

  • 客戶端指定的命令
  • 定位entry在日誌中位置的索引(索引從1開始)
  • entry被覆寫時識別的任期數

Raft需要複製所有的entry到所有follower節點來保持日誌的一致性。leader並行的發佈AppendEntries RPC到其它所有節點。leader會重試此操作直到所有的follower安全的複製了新的entry。

leader創建的entry複製給了大多數服務器時,此次請求被認爲是提交了。所有之前的entry,包括早期leader創建的也會被認爲是提交了。leader一旦執行了提交,馬上會返回結果給客戶端(譯者注:此時並不一定是所有節點都處理了leader請求)。

leader維護它的日誌中最大被提交的索引,並且通過AppendEntries RPC發送給其它follower。一旦那些follower發現entry被提交了,它將會把entry按序應用於狀態機。

raft維護以下屬性,這些屬性構成了匹配屬性的log:

  • 如果不同日誌中的兩個條目具有相同的索引和術語,則它們存儲相同的命令
  • 如果不同日誌中的兩個條目具有相同的索引和術語,則前面所有條目中的日誌都相同

發送AppendEntries RPC時,leader包含在new entry之前的term number和index。如果follower在自己的日誌中找不到此項的匹配項,則拒絕追加new entry的請求。

這個一致性檢查讓leader得出結論,每當AppendEntries從一個follower成功返回時,它們都有相同的日誌,直到index包含在RPC中。

但是在遇到leader崩潰時,leader和follower的日誌可能會不一致。

在raft中,leader通過強制follower複製自己的日誌來處理不一致問題。這意味着follower日誌中衝突的entry將會被來自leader的日誌覆蓋。

leader會嘗試找到最近的與follower日誌匹配的索引,並且將會刪除額外的日誌和增加新日誌(譯者注:來自leader同步的日誌)。

leader將會維護nextindex,這個索引是下一個發送給其它follower的log entry的索引。當一個leader得到權利後,它會將它最近的index的下一個index初始化nextindex值。

無論何時follower對於AppendRPC返回一個失敗,leader會減少nextindex並且發佈另一個AppendRPC。最終,nextindex會到達一個日誌彙集的一個值(譯者注:就是leader最新的index)。AppendRPC最終會成功並且它會刪除無關的log(如果有)和從leader的log中新增log(如果有)。因此,AppendRPC確保了leader日誌和follower是一致的。

鑑於這個機制,leader不需要採取特殊的操作來讓follower保持一致性。leader只需要正常的操作,log會在一致性檢測失敗失敗後自動的彙集(譯者注:以上機制就能保證數據自動同步,不需要額外的特殊操作)。leader不會覆蓋或刪除它自己的日誌。

安全性

Raft會確保leader在任期內會提前所有之前任期的log。這是爲了確保所有日誌一致,並且狀態機執行相同的命令集所必需的。

在選舉期間,RequestVote RPC會包含candidate的日誌信息。如果follower發現它自己的log比candidate的日誌要新,那麼follower不會投票給這個candidate。

Raft通過比較日誌中最後一個log的索引和任期來確定兩個log中哪個log更爲最新。如果log的最後一個具有不同的term,則具有後面term的log更爲最新。如果log以相同的term結束,則以較長的log爲準

集羣成員

爲了確保在配置變更時的安全,不應該出現相同任期選舉出了兩個leader。不幸的是,任何從舊配置直接切換到新配置都是不安全的。

Raft使用兩階段方法來改變集羣成員。首先,先切換到一個稱爲聯合共識的中間配置。然後一旦提交,它立刻切換到新的配置。

聯合共識允許各個服務器在不同的時間在不同的配置之間進行轉換,而不會影響安全性。此外,聯合一致性允許集羣在整個配置更改期間繼續爲客戶機請求提供服務。

聯合共識連接瞭如下的新舊配置:

  • log 在兩個配置的所有節點之間被複制
  • 任何的節點都可以成爲leader
  • 同意要來自新舊配置的多數節點
  • 當領導者收到配置更改消息時,它存儲並複製join consensus C<old,new>的log。服務器總是使用其日誌中的最新配置來做出決策,即使它沒有提交。當雙方達成共識時,只有日誌中包含C<old,new>的服務器才能成爲領導者。

現在,leader可以安全地創建一個描述C的日誌條目並將其複製到集羣。同樣,此配置將在每臺服務器上看到時立即生效。當根據C的規則提交新配置時,舊配置不相關,並且可以關閉不在新配置中的服務器。

一個很棒的raft協議可視化演示在這裏

更多的材料,如演講、演示、相關論文和開源實現,可以在這裏找到

我只研究了組成Raft的基本算法和它提供的安全保證的細節。這篇論文包含了更多的細節,它是非常容易理解的,因爲作者的主要目標是理解。我絕對建議你讀它,即使你以前從未讀過其他的論文。

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