PBFT算法
PBFT算法可以工作在异步环境中,并且通过优化在早期算法的基础上把响应性能提升了一个数量级以上。
PBFT算法在保证活性和安全性(liveness & safety)的前提下提供了(n-1)/3的容错性。
(n-1)/3是怎么得来的?
先假设f=(n-1)/3,因为我们知道有f个作恶节点,所以我们必须在n-f个状态复制机的沟通内,就要做出决定(为什么是n-f?因为不知道这f个节点是故障还是恶意,如果全部是故障节点,那最多收到的回应就只有n-f个,所以考虑到这种情况就只能设最低n-f)。如果这f个节点都是作恶节点,也就是说它们都会回应,那我们就必须要在能回应的节点中保证正确节点占上风,就是n-f-f>f,于是n>3f,即f=(n-1)/3。
这里真的困扰了我很久。
PBFT的一次简单的完整流程:
- 客户端向主节点发送请求调用服务操作
- 主节点通过广播将请求发送给其他副本
- 所有副本都执行请求并将结果发回客户端
- 客户端需要等待f+1个不同副本节点发回相同的结果,作为整个操作的最终结果。
客户(Client)、主节点(Leader)、副本(replica)、备份(backup)
副本和备份的区别是,副本包含主节点,备份不包含。
接下来说PBFT中最为重要的三阶段协议,这是PBFT能够达成共识的关键。
三阶段协议:(pre-prepare、prepare、commit)
pre-prepare
首先,客户(Client)发送请求(request)给主节点,主节点接收请求之后,会发送一个pre-prepared消息,<<pre-prepare, v, n, d>, m>,v代表现在所在的视图、n代表请求的序号、m代表这个请求消息、d代表这个请求消息的摘要,这个pre-prepared消息会发送给所有备份(backup)。backup接收之后进行核对验证,这里是验证签名(消息的签名正确)、视图编号(是否是当前视图)、之前有没有接收过这个消息、消息序号n要在水线范围内(h到H),确认无误之后把这个消息写入日志。然后backup就会发送一个prepare消息给其他所有replica。
prepare
prepare消息<prepare, v, n, d, i>,这里i代表当前节点i,其他上面都说了。接收到prepare消息的replica还是要核对验证,这里验证的内容和上面是一样的,有一点不一样的是还要看预准备消息和准备消息是否一致。确认无误之后也是写入日志。这里有个小细节和上一步不同就是,需要接收2f个正确的prepare消息才能到下一步,这里的2f是包含自己在内的。下一步replica节点就会发送commit消息给其他所有replica。
commit
commit消息<commit, v, n, D(m), i>,一样的,接收到的replica就要核对验证,验证的内容也是大差不差。确认无误写入日志,只有当接收到2f+1个正确消息才会到下一步。下一步就结束了,已经达成共识了,接下来就是处理请求,发送执行结果。
接下来说下水线是啥
要说这个就要从checkpoint开始,checkpoint是PBFT为了节省空间设计的。从上面我们可以看到副本需要不断地往日志中写入信息,一个请求写入的信息就非常多,更何况还有很多的请求。所以就需要定时定期的清空日志,但是当视图切换的时候又需要同步日志,同步信息,所以什么时候清空日志就很重要。于是就有了checkpoint,你可以定义一个常数C,表示每C个请求就建立一个checkpoint,意思就是把当前这个请求设立为checkpoint。通俗来讲就是建档,在这存一个档,以便于以后如果出现了什么意外可以读档。当然,所有节点还需要对这个checkpoint进行验证,要至少2f+1个节点证明这个checkpoint可以,那这个checkpoint就变成了stable checkpoint。
这跟水线有什么关系?水线的低值h就是最近的checkpoint的序号,高值就是h+k,k是什么取决于你上面定义的常数C,k肯定不能比C小。于是我们就知道水线的目的其实就是为了确保错误的节点不会浪费太多的序号空间。

浙公网安备 33010602011771号