選舉Leader
1. 獲得超過半數服務器的投票,贏得選舉,成爲Leader
2. 另一臺服務器贏得選舉,並接收到對應的心跳,成爲Follower
3. 選舉超時,沒有任何一臺服務器贏得選舉,自增當前任期,重新發起選舉
同步日誌
安全性保證
安全性
到目前爲止描述的機制並不能充分的保證每一個狀態機會按照相同的順序執行相同的指令,例如:一個跟隨者可能會進入不可用狀態同時領導人已經提交了若干的日誌條目,然後這個跟隨者可能會被選舉爲領導人並且覆蓋這些日誌條目;因此,不同的狀態機可能會執行不同的指令序列。
1. 領導者追加日誌(Append-Only)
領導者永遠不會覆蓋已經存在的日誌條目;
日誌永遠只有一個流向:從領導者到追隨者;
2. 選舉限制:投票阻止沒有全部日誌條目的服務器贏得選舉
如果投票者的日誌比候選人的新,拒絕投票請求;
這意味着要贏得選舉,候選者的日誌至少和大多數服務器的日誌一樣新,那麼它一定包含全部的已經提交的日誌條目。
3. 永遠不提交任期之前的日誌條目(只提交任期內的日誌條目)
在Raft算法中,當一個日誌被安全的複製到絕大多數的機器上面,即AppendEntries RPC在絕大多數服務器正確返回了,那麼這個日誌就是被提交了,然後領導者會更新commit index。
如果允許提交任期之前的日誌條目,那麼在步驟c中,我們就會把之前任期爲2的日誌提交到其他服務器中去,並造成了大多數機器存在了日誌爲2的情況。所以造成了d中S5中任期爲3的日誌條目會覆蓋掉已經提交的日誌的情況。
Raft 從來不會通過計算複製的數目來提交之前人氣的日誌條目。只有領導人當前任期的日誌條目才能通過計算數目來進行提交。一旦當前任期的日誌條目以這種方式被提交,那麼由於日誌匹配原則(Log Matching Property),之前的日誌條目也都會被間接的提交。
論文中的這段話比較難理解,更加直觀的說:由於Raft不會提交任期之前的日誌條目,那麼就不會從b過渡到c的情況,只能從b發生S5down機的情況下直接過渡到e,這樣就產生的更新的任期,這樣S5就沒有機會被選爲領導者了。
4. 候選者和追隨者崩潰
候選者和追隨者崩潰的情況處理要簡單的多。如果這類角色崩潰了,那麼後續發送給他們的 RequestVote和AppendEntries的所有RCP都會失敗,Raft算法中處理這類失敗就是簡單的無限重試的方式。
如果這些服務器重新可用,那麼這些RPC就會成功返回。如果一個服務器完成了一個RPC,但是在響應Leader前崩潰了,那麼當他再次可用的時候還會收到相同的RPC請求,此時接收服務器負責檢查,比如如果收到了已經包含該條日誌的RPC請求,可以直接忽略這個請求,確保對系統是無害的。
集羣成員變更
集羣成員的變更和成員的宕機與重啓不同,因爲前者會修改成員個數進而影響到領導者的選取和決議過程,因爲在分佈式系統這對於majority這個集羣中成員大多數的概念是極爲重要的。
簡單的做法是,運維人員將系統臨時下線,修改配置,重新上線。但是這種做法存在兩個缺點:
- 更改時集羣不可用
- 人爲操作失誤風險
直接從一種配置轉到新的配置是十分不安全的
如下圖所示:
因爲各個機器可能在任何的時候進行轉換。在這個例子中,集羣配額從 3 臺機器變成了 5 臺。不幸的是,存在這樣的一個時間點,兩個不同的領導人在同一個任期裏都可以被選舉成功。一個是通過舊的配置,一個通過新的配置。
兩階段方法保證安全性:
爲了保證安全性,配置更改必須使用兩階段方法。在 Raft 中,集羣先切換到一個過渡的配置,我們稱之爲共同一致;一旦共同一致已經被提交了,那麼系統就切換到新的配置上。共同一致是老配置和新配置的結合。
共同一致允許獨立的服務器在不影響安全性的前提下,在不同的時間進行配置轉換過程。此外,共同一致可以讓集羣在配置轉換的過程人依然響應服務器請求。
一個領導人接收到一個改變配置從 C-old 到 C-new 的請求,他會爲了共同一致存儲配置(圖中的 C-old,new),以前面描述的日誌條目和副本的形式。一旦一個服務器將新的配置日誌條目增加到它的日誌中,他就會用這個配置來做出未來所有的決定。領導人完全特性保證了只有擁有 C-old,new 日誌條目的服務器纔有可能被選舉爲領導人。當C-old,new日誌條目被提交以後,領導人在使用相同的策略提交C-new,如下圖所示,C-old 和 C-new 沒有任何機會同時做出單方面的決定,這就保證了安全性。
一個配置切換的時間線。虛線表示已經被創建但是還沒有被提交的條目,實線表示最後被提交的日誌條目。領導人首先創建了 C-old,new 的配置條目在自己的日誌中,並提交到 C-old,new 中(C-old,new 的大多數和 C-new 的大多數)。然後他創建 C-new 條目並提交到 C-new 中的大多數。這樣就不存在 C-new 和 C-old 可以同時做出決定的時間點。
日誌壓縮
日誌會隨着系統的不斷運行會無限制的增長,這會給存儲帶來壓力,幾乎所有的分佈式系統(Chubby、ZooKeeper)都採用快照的方式進行日誌壓縮,做完快照之後快照會在穩定持久存儲中保存,而快照之前的日誌和快照就可以丟棄掉。
Raft的具體做法如下圖所示:
與Raft其它操作Leader-Based不同,snapshot是由各個節點獨立生成的。除了日誌壓縮這一個作用之外,snapshot還可以用於同步狀態:slow-follower以及new-server,Raft使用InstallSnapshot RPC完成該過程,不再贅述。
Client交互
- Client只向領導者發送請求;
- Client開始會向追隨者發送請求,追隨者拒絕Client的請求,並重定向到領導者;
- Client請求失敗,會超時重新發送請求;
Raft算法要求Client的請求線性化,防止請求被多次執行。有兩個解決方案:
- Raft算法提出要求每個請求有個唯一標識;
- Raft的請求保持冪等性;
自增當前任期?當前任期結束,新的任期開始。
當投票被瓜分後,所有的candidate同時超時,然後有可能進入新一輪的票數被瓜分,爲了避免這個問題。每個candidate的election timeout從150ms-300ms之間隨機取。這樣candidate發起新一輪的leader election的時間就會相同,誰的election timeout小誰成爲leader的機率就大。
在Raft中,問題分解爲:領導選取、日誌複製、安全和成員變化。
基本概念
複製狀態機(Replicated State Machine)
複製狀態機通過複製日誌來實現:
一致性算法作用於一致性模型,一般有以下特性:
服務器狀態
每臺服務器一定會處於三種狀態:
追隨者只響應其他服務器的請求。如果追隨者沒有收到任何消息,它會成爲一個候選人並且開始一次選舉。收到大多數服務器投票的候選人會成爲新的領導人。領導人在它們宕機之前會一直保持領導人的狀態。
任期(Term)
Raft 算法將時間劃分成爲任意不同長度的任期(term)。任期用連續的數字進行表示。每一個任期的開始都是一次選舉(election),一個或多個候選人會試圖成爲領導人。如果一個候選人贏得了選舉,它就會在該任期的剩餘時間擔任領導人。在某些情況下,選票會被瓜分,有可能沒有選出領導人,那麼,將會開始另一個任期,並且立刻開始下一次選舉。Raft 算法保證在給定的一個任期最多隻有一個領導人。
RPC
Raft 算法中服務器節點之間通信使用遠程過程調用(RPCs),並且基本的一致性算法只需要兩種類型的 RPCs。請求投票(RequestVote) RPCs 由候選人在選舉期間發起,然後附加條目(AppendEntries)RPCs 由領導人發起,用來複制日誌和提供一種心跳機制。爲了在服務器之間傳輸快照增加了第三種 RPC。當服務器沒有及時的收到 RPC 的響應時,會進行重試, 並且他們能夠並行的發起 RPCs 來獲得最佳的性能。
RPC有三種:
超時設置:
BroadcastTime << ElectionTimeout << MTBF
兩個原則:
一般BroadcastTime大約爲0.5毫秒到20毫秒,ElectionTimeout一般在10ms到500ms之間。大多數服務器的MTBF都在幾個月甚至更長。
領導人選取
觸發條件:
候選操作過程:
注意事項:
問題探討:爲什麼這裏沒有談收到其他候選者的RequestVote RPC請求?
可能的解釋:
日誌複製
接受命令的過程:
提交過程: