PBTF共识机制
簡介
實用拜占庭容錯 (Practical Byzantine Fault Tolerance, PBFT) 算法是Miguel Castro和Barbara Liskov發表于1999年OSDI(Operating Systems Design and Implementation)會議的研究成果。PBFT描述了一種解決拜占庭容錯問題的副本復制算法,解決了原始拜占庭容錯算法效率不高的問題,將算法復雜度由指數級降低到多項式級,使得拜占庭容錯算法在實際系統應用(異步環境)中變得可行。
PBFT是一種狀態機副本復制算法,即服務作為狀態機進行建模,狀態機在分布式系統的不同節點進行副本復制。每個狀態機的副本都保存了服務的狀態,同時也實現了服務的操作。將所有的副本組成的集合使用大寫字母R表示,使用0到|R|-1的整數表示每一個副本。為了描述方便,假設|R|=3f+1,這里f是有可能失效的副本的最大個數。盡管可以存在多于3f+1個副本,但是額外的副本除了降低性能之外不能提高可靠性。
所有的副本在一個被稱為視圖(View)的輪換過程中運作。在某個視圖中,一個副本作為leader節點(也稱primary主節點),其他的副本作為follower節點(也稱backup備份節點)。視圖是連續編號的整數。leader節點由公式p = v mod |R|計算得到,這里v是視圖編號,p是副本編號,|R|是副本集合的個數。當leader節點失效的時候就需要啟動視圖更換(view change)過程。
主節點由公式p = v mod |R|計算得到,這里v是視圖編號,p是副本編號,|R|是副本集合的個數。當主節點失效的時候就需要啟動視圖更換(view change)過程。
算法步驟
(1)取一個副本作為主節點,其他的副本作為備份;
(2)用戶端向主節點發送使用服務操作的請求;
(3)主節點通過廣播將請求發送給其他副本;
(4)所有副本執行請求并將結果發回用戶端;
(5) 用戶端需要等待F+1個不同副本節點發回相同的結果,作為整個操作的最終結果。
image.png算法分為三個階段:pre-prepare、prepare 和 commit。Pre-prepare和Prepare保證同一個view中的請求有序,Prepare和Committed保證不同view中的請求被有序地執行。
pre-prepare階段
目的:作為一種證明,確定該請求是在視圖v中被賦予了序號n,從而在視圖變更的過程中可以追索。
當primary節點收到請求時,則開始pre-prepare階段:primary對收到的請求標記序號n(n是按序增長的),把pre-prepare消息進行廣播,并把此消息記入自己的log中。消息格式為<PRE-PREPARE,v,n,m>,v是primary節點當前的視圖號,n是這個請求的序號,m是請求。
Backup節點收到<PRE-PREPARE,v,n,m>消息時進行檢查,需滿足的條件:
(1)請求和預準備消息簽名正確,并且d與m摘要一致;
(2)當前視圖編號是v;
(3)該備份節點從未在視圖v中接受過序號為n但是摘要d不同的消息m;
(4)預準備消息的序號n必須在水線(watermark)上下限h和H之間。
完成標志:
若備份節點i接受了預準備消息,則進入prepare階段。
prepare階段
一個backup(i)節點接受<PRE-PREPARE,v,n,m>消息后,則進入prepare階段,該節點i向所有副本節點發送準備消息,消息格式為<PREPARE,v,n,d,i>,其中d是m的digest,i把<PRE-PREPARE,v,n,m>消息和<PREPARE,v,n,d,i>消息都記入自己的log中。
如果一個節點收到2f個來自不同節點且與PRE-PREPARE匹配的<PREPARE,v,n,d,i>消息時(匹配的條件是要有相同的v和相同的n),我們把這2f個<PREPARE,v,n,d,i>消息作為prepared的證明。
完成的標志:副本節點將(m,v,n,i)記入消息日志。
接受準備消息需滿足的條件:
(1)每個副本節點驗證預準備和準備消息的一致性主要檢查:視圖編號v、消息序號n和摘要d。
(2)預準備階段和準備階段確保所有正常節點對同一個視圖中的請求序號達成一致。(對此結論的形式化證明:如果prepared(m,v,n,i)為真,則prepared(m`,v,n,j)必不成立,這就意味著至少f+1個正常節點在視圖v的預準備或者準備階段發送了序號為n的消息m)
commit階段
如果一個節點(i)得到了prepared的證明,則廣播commit消息到每一節點,并把此commit消息記入log中。消息格式為<COMMIT,v,n,D(m),i>。如果一個節點收到2f+1個來自不同節點且有相同的n、相同的v、相同的d的<COMMIT,v,n,D(m),i>消息,我們把這2f+1個commit消息作為committed的證明。當一個請求被提交到一個節點,這個節點執行請求,并把結果返回給客戶端。節點保存的有上一個返回給客戶端的應答(last reply),從中可以得到last reply的時間戳,節點不接受時間戳小于last reply的時間戳的請求。
每個副本接受確認消息的條件是:1)簽名正確;2)消息的視圖編號與節點的當前視圖編號一致;3)消息的序號n滿足水線條件,在h和H之間。一旦確認消息的接受條件滿足了,則該副本節點將確認消息寫入消息日志中。(補充:需要將針對某個請求的所有接受的消息寫入日志,這個日志可以是在內存中的)
接受確認消息需要滿足的條件:
我們定義確認完成committed(m,v,n)為真得條件為:任意f+1個正常副本節點集合中的所有副本i其prepared(m,v,n,i)為真;本地確認完成committed-local(m,v,n,i)為真的條件為:prepared(m,v,n,i)為真,并且i已經接受了2f+1個確認(包括自身在內)與預準備消息一致。確認與預準備消息一致的條件是具有相同的視圖編號、消息序號和消息摘要。
確認被接受的形式化描述:
確認階段保證了以下這個不變式(invariant):對某個正常節點i來說,如果committed-local(m,v,n,i)為真則committed(m,v,n)也為真。這個不變式和視圖變更協議保證了所有正常節點對本地確認的請求的序號達成一致,即使這些請求在每個節點的確認處于不同的視圖。更進一步地講,這個不變式保證了任何正常節點的本地確認最終會確認f+1個更多的正常副本。
image.png視圖變更
使用計時器的超時機制觸發視圖變更事件,以防止備份節點無期限地等待請求的執行。視圖變更協議在主節點失效的時候仍然保證系統的活性。視圖變更可以由超時觸發,以防止備份節點無期限地等待請求的執行。備份節點等待一個請求,就是該節點接收到一個有效請求,但是還沒有執行它。當備份節點接收到一個請求但是計時器還未運行,那么它就啟動計時器;當它不再等待請求的執行就把計時器停止,但是當它等待其他請求執行的時候再次情動計時器。
如果備份節點的計時器在視圖中超時,備份節點啟動視圖變更以將系統變更到視圖v+1。它停止接受消息(除了檢查點,視圖變更和新視圖消息)并廣播<view-change,v+1,n,C,P,i>消息到所有follower節點。這里n是節點i已知的最新穩定檢查點s的序列號,C是2f+1個有效的證明了s正確性的檢查點消息集合,P是在節點i已經準備好(prepared)的序列號高于n的請求所對應的Pm的集合。每個Pm包含有效的預準備消息(不包含相應的客戶端消息)和2f個來自不同節點的與之相匹配的有效的準備消息。
當新leader節點從其他follower節點接收到2f個有效視圖變更消息時,它會將<new-view,v+1,V,O>消息廣播到其他follower節點,其中V是包含leader節點接收到的有效視圖變更消息加上leader節點發送的視圖變更消息(或將已發送),O是一組預準備消息(沒有捎帶請求)。O計算如下:1、leader節點確定最新的穩定檢查點的序列號min-s和準備消息中的最高序列號max-s。2、leader節點為視圖v+1創建一個新的預準備消息,其序號n在min-s和max-s之間。有兩種情況: (1)在V中的一些視圖變更消息的P分量中至少有一個集合具有序列號n,或者(2)沒有這樣的集合。在第一種情況下,主要創建一個新的消息<PRE-PREPARE ,v+1,n,d>,其中d是預備消息中的請求摘要,該請求具有最高視圖編號的序列號n。在第二種情況下,它創建一個新的預準備消息<PRE-PREPARE ,v+1,n,d-null>,其中d-null是特殊空請求的摘要; 空請求像其他請求一樣通過協議,但其執行是無操作的。(使用類似的技術填補空白。)
接下來,leader節點將O中的消息插入到它的日志中。如果min-s大于其最新的穩定檢查點的序列號,leader節點也將具有序列號min-s的檢查點穩定性的證明插入日志中,并丟棄來自日志中無異議消息記錄。然后進入視圖v+1:這時它能夠接受視圖v+1的消息。
一個備份節點接受新視圖消息的條件是,如果簽名正確,并且包含的視圖變更消息對于視圖v+1有效,并且集合O是正確的。它通過執行類似于leader節點使用的創建O的計算來驗證O的正確性。然后,將新信息添加到其日志中,并且為O中的每個消息廣播準備消息,并且將這些準備消息添加到其日志中,并進入視圖v+1。此后,協議正常進行。副本重做min-s和max-s之間的消息協議,但是它們避免重新執行客戶端請求(通過使用他們存儲的關于發送給每個客戶端的最后一個答復的信息)。
副本可能缺少一些請求消息或穩定的檢查點(因為這些不會在new-view消息中發送)。它可以從另一個副本獲取丟失的信息。例如,副本i可以從某些檢查點消息認證了檢查點s的正確性的副本之一獲取缺少的檢查點狀態。由于這些副本中的f+1個是正確的,所以副本將始終擁有檢查點s或稍后被認證的穩定檢查點。我們可以通過分塊狀態避免發送整個檢查點,并用修改它的最后一個請求的序列號對每個分區進行標記。為了使副本更新,只需要發送過期的分區,而不是整個檢查點。
垃圾回收
為了節省內存,系統需要一種將日志中的無異議消息記錄刪除的機制。為了保證系統的安全性,副本節點在刪除自己的消息日志前,需要確保至少f+1個正常副本節點執行了消息對應的請求,并且可以在視圖變更時向其他副本節點證明。另外,如果一些副本節點錯過部分消息,但是這些消息已經被所有正常副本節點刪除了,這就需要通過傳輸部分或者全部服務狀態實現該副本節點的同步。因此,副本節點同樣需要證明狀態的正確性。
在每一個操作執行后都生成這樣的證明是非常消耗資源的。因此,證明過程只有在請求序號可以被某個常數(比如100)整除的時候才會周期性地進行。我們將這些請求執行后得到的狀態稱作檢查點(checkpoint),并且將具有證明的檢查點稱作穩定檢查點(stable checkpoint)。
副本節點保存了服務狀態的多個邏輯拷貝,包括最新的穩定檢查點,零個或者多個非穩定的檢查點,以及一個當前狀態。寫時復制技術可以被用來減少存儲額外狀態拷貝的空間開銷。
檢查點的正確性證明的生成過程如下:當副本節點i生成一個檢查點后,向其他副本節點廣播檢查點消息<CHECKPOINT,n,d,i>,這里n是最近一個影響狀態的請求序號,d是狀態的摘要。每個副本節點都默默地在各自的日志中收集并記錄其他節點發過來的檢查點消息,直到收到來自2f+1個不同副本節點的具有相同序號n和摘要d的檢查點消息。這2f+1
個消息就是這個檢查點的正確性證明。
具有證明的檢查點成為穩定檢查點,然后副本節點就可以將所有序號小于等于n的預準備、準備和確認消息從日志中刪除。同時也可以將之前的檢查點和檢查點消息一并刪除。
檢查點協議可以用來更新水線(watermark)的高低值(h和H),這兩個高低值限定了可以被接受的消息。水線的低值h與最近穩定檢查點的序列號相同,而水線的高值H=h+k,k需要足夠大才能使副本不至于為了等待穩定檢查點而停頓。加入檢查點每100個請求產生一次,k的取值可以是200。
算法優化
(1)簡化回復(digest replies)
客戶端只需要一個備份節點發送完整回復消息,其余備份節點只需發送回復消息的摘要。可獲取完整回復以驗證正確性,同時減輕網絡帶寬消耗。
(2)執行優化(減少消息延遲)
(3)優化只讀操作(read-only operations)
不改變服務狀態
作者:vdes
鏈接:https://www.jianshu.com/p/bce378cf5821
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
總結
- 上一篇: 可验证随机函数VRF之Algorand算
- 下一篇: CBFT共识机制