拜占庭将军问题是指系统中除了网络延迟、系统宕机等问题外还存在恶意节点,会进行“精神分裂式”投票。
BFT(Byzantine Fault Tolerance)系统是指能够容忍拜占庭将军问题的系统,而PBFT(Practical Byzantine Fault Tolerance)则是其具体实现算法。其主旨是:当存在f个失效节点时必须保证存在至少3f+1个副本数量,这样才能保证在异步系统中提供安全性和活性。
那为什么是3f+1个副本呢?假设总的副本数是N,已知最多f个错误节点,所有节点都可能因为系统、网络等原因而无法回复消息,这类节点称为故障节点;而错误节点可能故意不回复消息,或者回复错误消息,或者像不同节点回复不同的消息。故障节点和错误节点统称为有问题节点。
此时会有两个极端:
f个错误节点也是故障节点,那么正确节点只需f+1个即可,系统至少需要2f+1个节点副本。
错误节点和故障节点都是不同的节点,因为知道f个节点的回复是不靠谱的,那么至少要收到N-f个回应后才能判断出结果。而由于到达顺序的问题,f个错误节点可能比正确节点先返回消息,所以收到的N-f个消息中能保证N-2f个消息一定是正确节点发来的。要求N-2f>f,所以N>3f。
尽管可以存在多于3f+1个副本,但是额外的副本除了降低性能之外不能提高可靠性。
三阶段流程
pbft算法的基本流程主要有以下四步:
客户端发送请求给主节点。
主节点通过广播将请求发送给其他副本(三阶段协议)。
节点处理完三阶段流程后,返回消息给客户端。
客户端收到来自f+1个不同节点的相同消息后,代表共识已经正确完成。
为什么收到f+1个不同节点的相同消息就代表共识已经正确完成?
从之前的推导可知,无论是最好的情况还是最坏的情况,只要客户端收到f+1个节点的相同回复消息,那么就代表有足够多的正确节点已全部达成共识并处理完毕了。
三阶段协议分为预准备(pre-prepare)、准备(prepare)和确认(commit)这三个阶段。
当主节点收到请求,向其他节点广播pre-prepare消息时,就开始了三阶段流程。
pre-prepare消息格式为<,m>,这里v是视图编号,通过对v取模可以得知当前的主节点;m是请求消息;n是主节点为请求消息分配的序列号;d是请求消息的摘要。
备份节点 i 收到pre-prepare消息后会进行验证,验证通过后备份节点就进入prepare阶段,广播prepare消息。
当备份节点 i 收到2f+1个一致的prepare消息时,节点进入commit阶段,广播commit消息。
当备份节点 i 收到2f+1个一致的commit消息时,表示大部分节点对这个客户端发来的请求达成了共识,节点执行请求操作,然后回复客户端。
预准备和准备两个阶段用来确保同一个视图中请求发送的时序性(即使对请求进行排序的主节点失效了),准备和确认两个阶段用来确保在不同的视图之间的确认请求是严格排序的。