圖片不能顯示時,請查看原文::https://lessisbetter.site/2020/03/15/why-pbft-needs-3-phase-message/
前言
在面試的時候,很多同學的簡歷熟悉PBFT共識算法,在現場面試的時候,卻只能說個主要邏輯,離完整的算法,還差十萬八千里,相似從網絡上看了一些文章,就算是熟悉了。當我問“爲什麼PBFT需要3個階段消息?2個階段行不行”時,還沒有人能回答出來。
回答這個問題,還要從PBFT要解決的本質問題說起,所以我打算以這樣一個思路,爲大家回答問題:
- PBFT與拜占庭問題
- 拜占庭節點在網絡中的行爲
- 什麼是3階段消息
- 3階段消息解決什麼問題
- 爲什麼不能只有前2個階段
- 論文使用的2個不變性
- 爲什麼3個階段可以達成一致性
PBFT與拜占庭問題
萊斯利·蘭波特在其論文[1]中描述瞭如下拜占庭問題:
一組拜占庭帝國的將軍分別各率領一支軍隊共同圍困一座城市。爲了簡化問題,將各支軍隊的行動策略限定爲進攻或撤離兩種。因爲部分軍隊進攻,或部分軍隊撤離可能會造成災難性後果,因此各位將軍必須通過投票來達成一致策略,即所有軍隊一起進攻或所有軍隊一起撤離。因爲各位將軍分處城市不同方向,他們只能通過信使互相聯繫。在投票過程中,每位將軍都將自己投票進攻還是撤退的信息,通過信使分別通知其他所有將軍,這樣一來每位將軍根據自己的投票,和其他所有將軍送來的信息,就可以知道共同的投票結果,而決定行動策略。
問題在於,將軍中可能出現叛徒(壞將軍),他們不僅可能向較爲糟糕的策略投票,還可能選擇性地發送投票信息。阻止好將軍達成一致的形成策略。
摘自:維基百科:拜占庭將軍問題,有刪改。
很多人喜歡玩狼人殺,我也喜歡,但我玩的很菜,我用狼人殺跟拜占庭將軍問題做個類比。
在狼人殺開局的時候,你是好人,並且不知道自己的隊友是誰,也不知道狼人是誰,但所有的好人都有一個共同的目的:乾死狼人,好人獲勝。所以遊戲中需要使用技巧和策略,達成目的。
拜占庭將軍問題是類似的,好的將軍不知道其他將軍是好的,還是壞的,但所有好的將軍的目的是:行動一致,共同進退。所以,它們也需要策略達成一致。
BFT是一類解決拜占庭將軍問題的策略/算法:讓非拜占庭節點達成一致的算法。在這類論文中,拜占庭節點指“壞”的將軍,非拜占庭節點指“好”的將軍。
PBFT是實用拜占庭算法(Practical Byzantine Fault Tolerance)的縮寫,該論文與1999年發表,另外2001年又發表了一篇Practical Byzantine Fault Tolerance and Proactive Recovery,讓PBFT擁有恢復能力。
PBFT作爲解決拜占庭問題的策略:非拜占庭節點不知道哪些是拜占庭節點,哪些是非拜占庭節點,PBFT要讓非拜占庭節點達成一致。
拜占庭節點在網絡中的行爲
拜占庭問題是在分佈式對等網絡,對通信容錯所提出來的。在真實世界中,拜占庭問題是什麼樣的?
通常使用拜占庭行爲,描述拜占庭節點可能的行爲,拜占庭行爲有:
- 任何不遵守協議的動作
- 惡意代碼、節點
- 代碼bug
- 網絡故障、數據包損壞
- 磁盤崩掉、重複丟失
- 無權限時加入
- …
什麼是3階段消息
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-qmyy4RN7-1584263615373)(http://img.lessisbetter.site/2020-03-pbft-3-phase-message.png)]
3階段消息是:Pre-prepare、Prepare和Commit。每個消息都會包含數字簽名,證明消息的發送者,以及消息類型,下文中會省略。
Pre-prepare消息由主節點發出,包含:
- 當前view:v
- 主節點分配給請求的序號n
- 請求的摘要d
- 請求本身m
務必記牢,m、v、n、d,後面會使用縮寫。
Prepare是副本節點收到Pre-prepare消息後,做出的響應,發送給所有副本節點,包含:
- v
- n
- d
Prepared狀態:副本i有Pre-prepare消息,且收到2f個有效的Prepare消息。
副本i達到Prepared狀態,可以發送Commit消息,Commit消息的內容和Prepare消息內容相同,但消息類型和數字簽名是不同的,所以可以區分。
m可以使用d代替,所以Prepare和Commit消息使用d代替m,來節省通信量。
3階段消息解決什麼問題
前面提到,PBFT解決的是拜占庭問題的一致性,即讓非拜占庭節點達成一致。更具體的說:讓請求m,在view內使用序號n,並且完成執行m,向客戶端發送響應。
爲什麼不能只有前2個階段消息
這個問題的等價問題是:爲什麼Pre-prepare和Prepare消息,不能讓非拜占庭節點達成一致?
Pre-prepare消息的目的是,主節點爲請求m,分配了視圖v和序號n,讓至少f+1個非拜占庭節點對這個分配組合<m, v, n>
達成一致,並且不存在<m', v, n>
,即不存在有2個消息使用同一個v和n的情況。
Prepared狀態可以證明非拜占庭節點在只有請求m使用<v, n>
上達成一致。主節點本身是認可<m, v, n>
的,所以副本只需要收集2f個Prepare消息,而不是2f+1個Prepare消息,就可以計算出至少f個副本節點是非拜占庭節點,它們認可m使用<v, n>
,並且沒有另外1個消息可以使用<v, n>
。
既然1個<v, n>
只能對應1個請求m了,達到Prepared狀態後,副本i執行請求m,不就達成一致了麼?
並不能。Prepared是一個局部視角,不是全局一致,即副本i看到了非拜占庭節點認可了<m, v, n>
,但整個系統包含3f+1個節點,異步的系統中,存在丟包、延時、拜占庭節點故意向部分節點發送Prepare等拜占庭行文,副本i無法確定,其他副本也達到Prepared狀態。如果少於f個副本成爲Prepared狀態,然後執行了請求m,系統就出現了不一致。
所以,前2個階段的消息,並不能讓非拜占庭節點達成一致。
如果你瞭解2PC或者Paxos,我相信可以更容易理解上面的描述。2PC或Paxos,第一步只是用來鎖定資源,第2步纔是真正去Do Action。把Pre-prepare和Prepare理解爲第一步,資源是<v, n>
,只有第一步是達不成一致性的。
2個不變性
PBFT的論文提到了2個不變性,這2個不變性,用來證明PBFT如何讓非拜占庭節點達成一致性。
第1個不變性,它是由Pre-prepare和Prepare消息所共同確保的不變性:非拜占庭節點在同一個view內對請求的序號達成共識。關於這個不變性,已經在爲什麼不能只有前2個階段消息中論述過。
介紹第2個不變性之前,需要介紹2個定義。
- committed-local:副本i已經是Prepared狀態,並且收到了2f+1個Commit消息。
- committed:至少f+1個非拜占庭節點已經是Prepared狀態。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-ppuxDV7L-1584263615375)(http://img.lessisbetter.site/2020-03-pbft-committed.png)]
第2個不變性,如果副本i是committed-local,那麼一定存在committed。
2f+1個Commit消息,去掉最多f個拜占庭節點僞造的消息,得出至少f+1個非拜占庭節點發送了Commit消息,即至少f+1個非拜占庭節點是Prepared狀態。所以第2個不變性成立。
爲什麼3個階段消息可以達成一致性
committed意味着有f+1個非拜占庭節點可以執行請求,而committed-local意味着,副本i看到了有f+1個非拜占庭節點可以執行請求,f+1個非拜占庭節點執行請求,也就達成了,讓非拜占庭節點一致。
雖然我前面使用了2PC和Paxos做類比,但不意味着PBFT的Commit階段就相當於,2PC和Paxos的第2步。因爲2PC和Paxos處理的CFT場景,不存在拜占庭節點,它們的主節點充當了統計功能,統計有多少節點完成了第一步。PBFT中節點是存在拜占庭節點的,主節點並不是可靠(信)的,不能依賴主節點統計是否有f+1個非拜占庭節點達成了Prepared,而是每個節點各自統計,committed-local讓節點看到了,系統一定可以達成一致,纔去執行請求。
總結
本文介紹了2個階段消息是無法達成一致的原因,而爲什麼3階段消息可以。最核心的還是要理解好,PBFT解決了什麼問題,以及它是如何解決的。
PBFT解決的是在拜占庭環境下,如何提供一致性,以及如何持續的提供一致性的問題。本文只介紹瞭如何提供一致性,沒有提如何持續提供一致性,即PBFT的可用性。現在,不妨思考一下,View Change是如何保證切換時一致性的,是否也需要2個不變性的支持呢?