PBFT

PBFT,即实用拜占庭容错。本文不赘述 BFT 相关概念,而是简述论文的具体实现部分。

默认情况下的 PBFT

假设节点数量为 3f+1,每个节点需要维护一致的状态机,为了能够迅速达成共识,需要有一个主节点,其他节点则作为从节点,主节点带领其他节点共识的这段时间称为 view,有点像 Raft 的任期,是自增的。

client 的请求也只会发送给主节点,由主节点进行集群的共识,期间需要经历 3 个阶段,集群的节点才能返回结果给 client。 如果 client 没有及时收到结果,就会给所有的节点发送请求,如果请求已经被处理,那么节点只会再次返回结果;否则,如果节点不是主节点,就会转发给主节点, 如果主节点并没有进行 pre-prepare phase,就会进行 view change。

three phase

三个阶段分为是 pre-prepare, prepare, commit,前两者的作用是在同样的 view 内对请求进行排序和验证,让非故障节点对请求的处理顺序达成一致。

pre-prepare

主节点收到 client 的请求 m 后,进入 pre-prepare phase,首先是主节点 p 广播消息给所有从节点。

从节点收到 pre-prepare 后,会对其进行验证,只有满足以下条件才能通过:

  1. m 的 hash 和消息的 hash 都是正确的(m 的 hash 包含在里面)
  2. 从节点 view 和 pre-prepare 的 view 一致
  3. 第一次收到这个消息
  4. 消息的 id 在检查范围(节点预设的记录区间,下文 有提到)之间

prepare

从节点通过了某条 pre-prepare 消息后,进入 prepare 阶段,并广播 prepare 消息。

同时也会验证其他节点的 prepare 消息,满足以下条件才能通过:

  1. prepare 消息的 hash 无误
  2. 从节点 view 和 prepare 的 view 一致
  3. 消息的 id 在检查范围之间

commit

当从节点接收到关于 m 的 2f(不包括自己) 条 hash、view 以及序列号都一致的 prepare 消息后就会进入 commit 阶段,并广播 commit 消息。

与 prepare 的验证类似,需要满足以下条件:

  1. commit 消息的 hash 无误
  2. 从节点 view 和 commit 的 view 一致
  3. commit 消息的 id 在检查范围之间

当从节点接收到关于 m 的 2f(不包括自己) 条 hash、view 以及序列号都一致的 commit 消息后就将结果更新到自己的状态机中并响应 client 了。

客户端收到了 f+1 个相同的返回结果后,就能将其作为最终的结果。

GC

历史数据比较多的时候,可以通过 checkpoint 创建检查点(类似于 Raft 的 snapshot),让 2f 个其他节点认可 stable checkpoint 后,再丢弃历史数据(包括所有日志 id 小于检查点的 pre-prepare,prepare 和 commit 消息),并且会根据最后一个 stable checkpoint 对应的 id 最为 low-water,设定一个数值(论文为 200+low-water,两个检查点的距离)作为 high-water,控制 three phase 的 id 在范围之间。

view change

因为 PBFT 高度依赖于主节点,作为拜占庭算法,需要额外考虑主节点的正确性,就有了一个和 Raft 的领导人选举类似的机制——view change,其触发的场景为:

  1. prepare 或 commit 后一定的时间内没收到 2f 条同样的消息
  2. 主节点不响应客户端的请求

当从节点触发 view change 的时候,停止接受除了 checkpoint、view-change 和 new-view 之外的各种消息,发送 view-change 给集群的其他成员,view change 消息中还包括了一系列信息,以证明视图更改的合理性和需要迁移的数据的正确性:

  • n:最后一个稳定检查点的序列号。
  • C:证明检查点正确性的两个检查点消息的集合。
  • P:对于每个该备份节点已经准备(prepared)的、且序列号高于 n 的请求,包含一个集合,每个集合包含一个有效的 pre-prepare 消息(没有相应的客户端消息),以及两个由不同备份节点签名的、有效的、具有相同视图、序列号和摘要的匹配 prepare 消息。
  • v+1:当前视图+1
  • i:节点 i

发送 view-change 的节点收到了 2f 条响应的时候,广播 new-view 给其他节点,内容包括:

  1. 其他节点同意 view-change 的消息集合
  2. 最新的 checkpoint 到最高的 prepared 消息下标的所有消息创建一个 pre-prepare 消息,如果这个消息在自己的消息队列存在,说明需要进行处理,正常发送 pre-prepare 消息;如果这个消息不在消息的队列中,说明这个消息已经被成功处理,则创建一个携带 null hash 的 pre-prepare 消息,不会改变从节点的状态机。
updatedupdated2024-04-302024-04-30