raft演算法解析
在尋找一種易於理解的一致性演算法的研究(In Search of an Understandable Consensus Algorithm-extended version) 論文中,作者提出raft演算法主要用來在分散式環境下管理日誌的狀態複製。為了解決paxos演算法的難於理解,raft演算法中給server引入了三個角色(狀態),分別為: Leader、follower、cadidate。並將一致性演算法劃分成三個子問題來解決:Leader 選舉、日誌複製、安全性。叢集中任何一個server在某一時刻只能處於一種角色狀態且只能有一個server處於Leader角色狀態。客戶端所有的寫請求都發送給Leader或由follower轉發給leader,由Leader將寫請求日誌複製到所有follower結點來保證結點間資料的一致性。當Leader結點出現故障,或者follower與Leader之間的心跳檢測中斷時,進行Leader的重新選擇。下面將分別從三個方面來詳細討論raft演算法的處理流程以及在安全性上採取的應對策略。
二、leader 選舉
2.1 leader選舉面臨的問題
Raft演算法通過選舉的方式,選出一個高貴的領導人來協調多結點資料的一致性。所有客戶端的請求日誌條目(寫請求)都轉發到leader伺服器上,由leader將日誌條目複製到其他的伺服器上,從而達到資料的一致性。這樣做大大簡化了資料一致性的管理。通過這種方式需要面臨若干問題:
1. 當leader宕機後,如何保證叢集繼續對外提供服務。(leader選舉)
2. 如何能夠儘快選出可用的leader。(通過大多數者同意能夠保證不受少數結點網路延遲的影響,並行傳送RPC選舉請求進一步提高效能)
3. leader將請求日誌複製到叢集中所有機器上時,效能問題如何解決。有些結點可能響應很慢或者掛掉。
4. follower宕機及宕機恢復後,日誌如何同步
針對問題1. Raft 使用心跳機制來觸發領導人選舉,如果一個follower在一段時間裡沒有接收到leader的心跳檢測訊息,此時他就會認為系統中沒有可用的領導者,並且發起選舉以選出新的領導者。
針對問題2. Raft採取的策略是大多數者同意的策略。即得到叢集中大多數者的投票同意,即認為選舉成功。這樣做有兩個好處,首先可以保證叢集中即使部分結點掛掉,或者部分結點響應很慢,不至於影響系統的可用性以及效能,為了進一步提高發送投票請求的效能,叢集中server向其他伺服器傳送投票請求時通過並行RPC的方式來進行傳送請求。
其次,採用大多數者同意策略可以保證在發生網路分割槽的情況下,不會產生多個Leader結點(即腦裂問題產生),從而可能導致資料的不一致問題。
2.2 叢集中server的三種狀態
下圖為raft中,叢集server的狀態轉換圖:
- 初始時,叢集server啟動,server狀態為follower。follower因超時時間未收到leader 或candidate的訊息而轉變為candidate,開始進入leader選主過程。
- cadidate狀態的server由於在當前一輪任期的選主過程中,未能選出leader重新進入下一輪任期leader選舉。
- cadidate在選舉的過程中可能收到leader已經選出的訊息或當前的自己記錄的任期資訊已經過期而進入follower狀態。
- cadidate由於收到大多數者的投票而成為leader,成為leader後會定期給其他server傳送心跳資訊以維護其領導者的權威。如果leader發現自己的任期號過期了,則進入follower角色狀態
raft演算法選舉的過程中,有個很重要的概念,即任期號(term)每個server都有一個term變數,該變數自增,每產生一個新的leader,term都會自增,用於表示,當前Leader是在該任期內產生的。
當follower轉變成cadidate時會自增當前的任期號,給自己投票,重置選舉超時計時器,然後通過並行RPC呼叫向其他server 傳送投票請求,請求過程中
攜帶4個引數:分別為 term(候選人任期號)、cadidateId(請求選票的候選人ID)、LastLogIndex(候選人的最後日誌條目的索引值)、LastLogTerm(候選人最後日誌條目任期號,即該條日誌請求命令是由哪個leader同步的)。返回值分別為:term (當前任期號,以便於候選人去更新自己的任期號),voteGranted(候選人贏得了此張選票時為真,否則為假)
如果cadidate獲得大多數者的同意,就變成leader,如果接收到新的leader的附加日誌RPC,轉變成follower。如果選舉過程超時即一段時間後沒有任何一個獲勝,再次發起新一輪選舉。cadidate獲得其他cadidate同意的條件是term最大,當term相同時,比較 cadidate之間的LastLogIndex,LastLogIndex大的獲勝。
當叢集中所有candidate狀態的server選舉超時,即該cadidate即沒有贏得選舉也沒有輸,會進行下一輪的選舉,這種情況可能重複一直髮生,以至於選票總是被瓜分從而沒有贏得大多數server的支援。raft演算法採用了隨機選舉超時時間的方式來確保選票瓜分的情況很少發生。選舉超時時間從一個固定區間(例如150-300毫秒)隨機選擇,進而使得每臺伺服器的超時時間儘量不相同,最先超時的cadidate,會新增term,進行新的投票,贏得投票,並在其他server超時前傳送心跳包。
三、日誌複製
3.1 狀態機
一致性演算法是從複製狀態機的背景下提出的。複製狀態機的結構如下圖所示。
叢集中每臺server都通過複製狀態機來保持各server狀態的一致性。server中的一致性模組接收客戶端的日誌命令請求,並將其按照順序寫入到日誌中,然後server與其他server一致性模組通訊,將日誌中的指令按照對應的順序複製到其他server,一旦複製完成,每個伺服器的狀態機按照日誌順序執行指令,並最終返回客戶端結果 ,因此伺服器叢集看起來像一個高可靠的狀態機
3.2 日誌
Leader處理客戶端的請求時,首先是將請求指令寫入到 Entrys日誌中。日誌如下所示:
圖中的每一個Entry日誌項包含了狀態機待執行的指令,以及該指令當前所處的任期。只有日誌條目被應用到狀態機中的時候,才能認為該日誌是已提交,即持久化。當Leader將日誌條目
複製到大多數機器的時候,該日誌條目即可被提交,並且該日誌條目之前的所有未提交的日誌條目也會被提交(包括其他領導人建立的條目,這主要是從安全性的角度考慮,加了這條規則,會在安全章節探討),並且在最終會被可用的狀態機執行
Raft日誌複製具有以下特性:
- 如果在不同的日誌中的兩個條目擁有相同的索引和任期號,那麼他們儲存了相同的指令。
- 如果在不同的日誌中的兩個條目擁有相同的索引和任期號,那麼他們之前的所有日誌條目也全部相同。
即領導人在該任期內建立的日誌條目存放在指定的日誌索引處,且該日誌條目的索引位置不會發生改變。
即Leader中的索引位置處的日誌條目與follower中對應索引處的日誌條目相同的話,則其索引之前的日誌條目同樣相同。這一點是通過follower的一致性檢查來保證的。
Leader在向follower傳送日誌複製請求時,會把之前日誌條目的索引與任期號一同包含在請求裡。如果follower未找到包含相同索引位置和任期號的條目,那麼他就會拒絕接收
新的日誌條目。
在Raft演算法中,領導人處理日誌不一致的方式是通過強制跟隨者複製自己的日誌來解決。這意味著跟隨者發生衝突的日誌會被領導者日誌所覆蓋。具體的處理方式。跟隨者會根據一致性檢查比較
領導者傳送上次日誌與索引位置是否相等,不相等,則會拒絕。領導者會修改nextIndex(領導者針對每一個跟隨者維護了一個nextIndex),將其值減1.在向follower傳送附加日誌請求。直到日誌匹配上後,直接將follower衝突的日誌覆蓋掉。
請求處理流程
- client 連線follower 或 leader ,如果client連線follower,則follower 將 client的(寫)請求轉發到leader,讀請求follower直接處理。
- leader接收到client請求,將該請求轉換成entry,寫入到自己的日誌中,得到在日誌中的index,然後會將該entry傳送給所有的follower(實際上是批量的entries)
- follower接收到leader的AppendEntries RPC請求後,會將leader傳過來的批量entires寫入到檔案中(通常並沒有立即重新整理到磁碟),然後向leader回覆OK
- leader收到過半的follower的OK回覆之後,就可以認為命令可以提交,然後根據日誌條目更新狀態機,leader更新commitIndex,更新狀態機完成後,回覆客戶端。
- 在下一次leader發給follower的心跳中,會將leader的commitIndex傳遞給follower,follower發現commitIndex更新了則也將commitIndex之前的日誌都進行提交併更新狀態機。
四、安全性
下面通過分析一種情行來看Raft是如何保證日誌複製的安全性的。
在a這個時間點,S1為Leader,進入b時間點後,複製日誌索引2位置日誌到S2,這時如果S1掛了,S5被選舉為Leader(通過S3,S4,S5的選票)。從客戶端接收了不一樣的日誌條目存放在索引2位置,進入到階段C,這時S5掛掉,S1已經恢復了,S1重新被選舉為Leader(通過S1,S2,S3),並複製任期2階段的日誌條目到大多數結點且未提交,這時S1掛了,s5重新被選舉為leader,進入階段d,並將自己任期內的日誌條目複製到叢集中其他結點,並覆蓋了索引2處的日誌。反之,如果在崩潰之前,S1 把自己主導的新任期裡產生的日誌條目複製到了大多數機器上,就如 (e) 中那樣,那麼在後面任期裡面這些新的日誌條目就會被提交(因為S5 就不可能選舉成功)。 這樣在同一時刻就同時保證了,之前的所有老的日誌條目就會被提交。
為了消除上圖中描述的情況,Raft 永遠不會通過計算副本數目的方式去提交一個之前任期內的日誌條目。只有領導人當前任期裡的日誌條目通過計算副本數目可以被提交;一旦當前任期的日誌條目以這種方式被提交,那麼由於日誌匹配特性,之前的日誌條目也都會被間接的提交。即在日誌複製的過程中,會從當前領導人當前的任期的日誌條目開始複製直到最近一次提交的日誌條目處。這樣能夠保證舊的任期的日誌不會被更新的任期的日誌條目所覆蓋。
參考:https://ramcloud.atlassian.net/wiki/download/attachments/6586375/raft.pdf