文章首發於公眾號:松花皮蛋的黑板報
作者就職於京東,在穩定性保障、敏捷開發、高階JAVA、微服務架構有深入的理解
分散式系統除了提升整個體統的效能外還有一個重要特徵就是提高系統的可靠性。提供可靠性可以理解為系統中一臺或多臺的機器故障不會使系統不可用或者丟失資料。保證系統可靠性的關鍵就是多副本,一旦有多副本,那麼就面臨多副本之間的一致性問題一致性演算法正是用於解決分散式環境下多副本之間資料一致性的問題的。業界最著名的一致性演算法就是大名鼎鼎的Paxos,但Paxos是出了名的難懂,而Raft正是為了探索一種更易於理解的一致性演算法而產生的,它將一致性拆分為leader選舉、日誌複製、安全性三個關鍵元素
1、leader選舉
Raft演算法將時間劃分成為任意不同長度的任期(term),任期用連續的數字表示,每一個任期的開始都是一次選舉。如果一個侯選人贏得了選舉,它就會在該任期的餘下時間擔任領導人,如果沒有選出領導人,將會開始另一個任期,並且立刻開始下一次選舉

那麼什麼時候開始選舉呢?實際上leader會向所有follower週期性傳送心跳,來保證它們的leader地位,如果一個follower在一個週期內沒有收到heartbeat資訊,那麼它就會假定沒有可用的leader,自已就轉換狀態成為候選人,並且開始一次選舉來選出一個新的leader
Raft選舉過程有三個規則
(1)、規則:如果請求投票的伺服器任期比自己的任期大,則給該伺服器投票

(2)、規則:在一個任期內,一臺伺服器最多能給一個侯選人投票,按照先到先得的原則

(3)、規則:隨機選舉超時
為了避免選票被平分,沒有伺服器成為leader,每臺伺服器在一個固定的範圍內隨機選取,從而使每臺伺服器的選舉超時時間均不相同,這種機制使得在大多數情況下只有一個伺服器會率先超時,它會在其他伺服器超時之前贏得選舉並且向其它伺服器傳送heartbeat資訊

當候選人收到大多數節點的選票則轉換為leader;發現leader或者收到更高任期的請求則轉換為follower,在角色轉換的時候遵守選舉安全原則,一個任期(term)內最多允許有一個leader被選上

2、日記複製
要理解為日記複製得先搞明白日記匹配原則和可被提交的日記這兩個概念
日記匹配原則描述的是,如果在不同日記中的兩個條目有著相同的索引和任期號,則它們儲存的命令是相同的。如果在不同日記中的兩個條目有著相同的索引和任期號,則它們之間的所有條目都是完全相同的
可被提交的日記描述的是,一旦被leader建立的條目已經複製到了大多數伺服器上,這個日記就稱為可被提交的。leader日記中之前的條目都是可被提交的,包括由之前的leader建立的條目

在日記複製流程中,leader需要找到follower同它的日記一致的地方,然後刪除follower在該位置之後的條目,然後將自己在該位置之後的條目傳送給follower

完整流程是,client向leader發出請求,leader將日記條目加入到它的日誌中,並行向follower伺服器發起日記複製請求,leader確認日記條目被安全複製後,將該條目應用到狀態機,然後向client返回結果,向follower發出可以提交該日記條目的請求。當一個伺服器已經將給它索引位置的日記條目應用到狀態機中,則所有其他伺服器不會在該索引位置應用不同的條目

在日記複製中,可能會出現各種各樣的異常
(1)、異常:資料傳送到leader,但未複製到follower

leader異常,client接收不到ack,重新選舉後,client可重新向leader發出請求
(2)、異常:資料到達leader節點,成功複製到follower所有節點,但未向leader響應接收

重新選舉後,雖然資料在follower節點處於未提交狀態但保持一致,重新選舉出leader後可完成資料提交,由於原leader異常,client無法接收到ack,會重新向leader發出請求,Raft要求RPC冪等,所以伺服器要實現去重機制
(3)、異常:資料到達leader節點,成功複製到follower所有或多數節點,資料在leader處於已提交狀態,但在follower處於未提交狀態

這個階段leader掛掉,重新選出新leader
(4)、異常:由於網路”分割槽”導致出現雙leader
以5個伺服器節點為例,正常情況下A節點為leader,接收client的請求,並將日記同步到B、C、D、E四個follower節點。由於網路原因,A、B節點無法與C、D、E進行通訊,C、D、E重新選舉,選擇leader,假話E節點勝出,此時出現雙leader。隨後網路恢復,A、B將做為E的follower
3、安全性
Raft通過比較日記中最後一個條目的索引和任期號來決定兩個日記哪一個更新,如果兩個日記的任期號不同,任期號大的更新,如果任期號相同,更長的日記更新
Raft為了保證安全性,約定了一個選舉限制,RequestVote RPC中包含候選人的日記資訊,如果伺服器自己的日記比候選人還要新,則會拒絕為該候選人投票

這個行為背後是leader完全原則,如果一個日記條目在一個給定任期內被提交,那麼這個條目一定會出現在所有任期號更大的leader中。所有的日誌條目都只會從leader節點往follower節點寫入,且leader節點上的日誌只會增加,絕對不會刪除或者覆蓋
文章來源:www.liangsonghua.me
作者介紹:京東資深工程師-樑鬆華,長期關注穩定性保障、敏捷開發、JAVA高階、微服務架構
