Raft保證當複製狀態機數量爲3f+1時, 最多能夠容許f個狀態機虛假。
一個view中只有一個primary 其餘爲副本。
視圖更改說明primary崩潰或失敗。算法
算法對節點的要求:安全
客戶端經過發送消息 <REQUEST,o,t,c>
到primary請求狀態機執行操做o。
t:時間戳用於確保該操做只執行一次,而且全部的請求都按照時間戳前後排序。
由節點發送到客戶端的消息包括(當前視圖號v,容許客戶端去跟蹤視圖發現當前的primary).優化
節點直接發送響應到客戶端,響應內容包括<REPLY,v,t,c,i,r>
.
v:當前視圖號。
t:響應請求的時間戳。
i:節點ID
r:執行操做獲得的結果。日誌
每個節點的狀態包括服務的狀態。消息日誌包括節點被接受的信息,以及節點當前的視圖。
當primary接受到客戶端的請求m,將開始三個階段的協議進行自動多播請求到節點。
除非消息的數量超出協議中給定的最大消息數量不然primary當即開始該三階段協議。若是消息超過最大消息數,將會將請求放置緩衝區。code
三階段分爲pre-prepare,prepare,commit
。blog
pre-prepare
和prepare
階段用於對在同一視圖中發送的請求徹底排序,即便提出請求排序的primary爲虛假節點也是如此。prepare
和commit
階段用於確保在視圖之間對提交的請求進行徹底排序 在pre-prepare
階段,primary定義了一個序列號n,到請求消息中。多播一個pre-prepare
消息並聯合消息m到全部節點。並將該消息添加到日誌中。該消息內容爲 <<PRE-PREPARE,v,n,d>_s,m>
(_s
表明簽名)這裏的v代表被髮送的消息處於的視圖。m是客戶端的請求消息。d爲m的摘要。
爲了保持消息較小。請求沒有包括在pre-prepare
消息中。這是很重要的由於pre-prepare
消息用於做爲該請求定義的序列號n在視圖v中的證實。另外,它將協議與協議徹底分離,以將請求傳輸到節點;容許咱們爲協議消息使用針對小消息優化的傳輸,對於大型請求針對大消息使用優化的傳輸。
節點接收到提供的pre-prepare消息後:排序
pre-prepare
消息是有效的,而且d是m的摘要。pre-prepare
消息中的序列號在低的閾值h與高閾值H之間。最後一個條件用於阻止錯誤的primary爲了耗盡序列號空間而選擇一個很是大的值。it
若是節點i接受了 <<PRE-PREPARE,v,n,d>_s,m>
消息。節點將會進入prepare
階段,並多播 <PREPARE,v,n,d,i>_s
消息到全部其餘的節點,並將該消息添加到它的日誌中。不然將什麼也不作。集羣
節點(包括primary)接收了prepare
消息:變量
並添加他們到本身的日誌中。
只有當節點i已將如下消息添加到它的日誌:
pre-prepare
消息(來自不一樣節點2f個) 而且節點經過檢查prepare
消息與pre-prepare
消息具備相同的視圖,序列號和簽名,才認爲prepared (m,v,n,i)
消息爲有效的。
算法的pre-prepare
和prepare
階段保證誠實節點贊成視圖中請求的總順序。更準確的,確保如下的變量:
prepared (m,v,n,i)
消息是有效的,那麼prepared (m’,v,n,j)
消息是無效的。而且任何D(m') 不等於D(m).prepared (m,v,n,i)
消息和 R=3f+1代表至少有f+1個誠實節點在視圖v中發送了序列號爲n的pre-prepare
消息或者是prepare
消息。prepared (m’,v,n,j)
消息若是是有效的,那麼須要至少一個誠實節點必須發送兩個衝突的prepare
消息(或者是視圖爲v的primary
發送pre-prepare
消息),兩個prepare
消息具備相同的視圖和序列號可是具備不一樣的摘要信息。可是這是不可能的由於節點不是虛假節點。 當prepared (m,v,n,i)
消息爲有效的那麼節點i多播 <COMMIT,v,n,D(m),i>_s
消息到其餘節點.這個過程爲commit
階段。節點接收commit
消息並添加該信息到日誌中。
若是而且只有當對於全部在f+1誠實節點中的節點i,prepared (m,v,n,i)
消息都是有效的,那麼committed (m,v,n,i)
消息則是有效的。
若是而且只有當節點i從不一樣的節點接收到2f+1個commit
消息(可能包括本身),而且與請求m的pre-prepare
消息匹配(具備相同的視圖,序列號和摘要)。則committed-local (m,v,n,i)
消息是有效的。
commit
階段確保如下變量:
committed-local (m,v,n,i)
消息是有效的。那麼committed(m,v,n)
消息是有效的。 每個節點i在當committed-local(m,v,n,i)
消息是有效的,而且i的狀態反應了在全部請求中該請求的序列號是最小的狀況下將會執行該操做。確保了全部誠實節點能夠以相同的順序執行請求,保證了安全性。在執行完請求操做後,節點將返回一個響應到客戶端。
當請求的時間戳小於最後一次回覆的時間戳時節點拋棄該請求。保證只執行一次。
不依賴消息順序交付。所以可能節點亂序提交請求。這是無所謂的,由於節點保持了pre-prepare
,prepare
,和commit
消息日誌一直到該請求被執行。
圖展現了該算法的以一種正常的例子(沒有primary虛假)的操做。節點0爲primary,節點3爲虛假節點。C爲客戶端.