详解实用拜占庭协议Pbft(二)
「详解实用拜占庭协议Pbft(一)」中介绍了Pbft
算法的正常流程,但是还有一些可用性方面的问题没有解决,比如日志无限增长,主节点故障,增删节点。
日志压缩
Pbft
算法在运行的过程中,日志会不断累积的,但是在实际的系统中,无论是从日志占用的磁盘空间,还是新节点加入集群,同步日志的网络消耗来看,日志都不能无限的增长。
Pbft
采用检查点(checkpoint)
机制来压缩日志,其本质和Raft
算法采用快照的形式清理日志是一样的,只是实现的方式不同。
为每一次操作创建一个集群中稳定检查点,代价是非常昂贵的,Pbft
为常数个操作创建一次稳定检查点,比如每100个操作创建一次检查点,而这个检查点就是checkpoint
,当这个checkpoint
得到集群中多数节点认可以后,就变成了稳定检查点stable checkpoint
。
当节点i
生成checkpoint
后会广播消息<CHECKPOINT, n, d, i>
其中n
是最后一次执行的消息序号,d
是n
执行后的状态机状态的摘要。每个节点收到2f+1
个相同n
和d
的checkpoint
消息以后,checkpoint
就变成了stable checkpoint
。同时删除本地序号小于等于n
的消息。
同时checkpoint
还有一个提高水线(water mark)
的作用,当一个stable checkpoint
被创建的时候,水线h
被修改为stable checkpoint
的n
,水线H
为h + k
而k
就是之前用到创建checkpoint
的那个常数。
视图切换(View-Change)
在正常流程中,可以看到所有客户端发来的消息m
都是由主节点p
广播到集群的,但是当主节点突然宕机,又怎么保证集群的可用性呢?
view-change
提供了一种当主节点宕机以后依然可以保证集群可用性的机制。view-change
通过计时器来进行切换,避免副本长时间的等待请求。
当副本收到请求时,就启动一个计时器,如果这个时候刚好有定时器在运行就重置(reset)定时器,但是主节点
宕机的时候,副本i
就会在当前视图
v中超时,这个时候副本i
就会触发view-change
的操作,将视图切换为v+1
。
- 副本
i
会停止接收除了checkpoint
,view-change
和new view-change
以外的请求,同时广播消息<VIEW-CHANGE, v+1, n, C, P, i>
的消息到集群。n
是节点i
知道的最后一个stable checkpoint
的消息序号。C
是节点i
保存的经过2f+1
个节点确认stable checkpoint
消息的集合。P
是一个保存了n
之后所有已经达到prepared
状态消息的集合。
- 当在视图( v+1 )中的主节点
p1
接收到2f
个有效的将视图变更为v+1
的消息以后,p1
就会广播一条消息<NEW-VIEW, v+1, V, Q>
V
是p1
收到的,包括自己发送的view-change
的消息集合。Q
是PRE-PREPARE
状态的消息集合,但是这个PRE-PREPARE
消息是从PREPARE
状态的消息转换过来的。
- 从节点接收到
NEW-VIEW
消息后,校验签名,V
和Q
中的消息是否合法,验证通过,主节点和副本都 进入视图v+1
。
当p1
在接收到2f+1
个VIEW-CHANGE
消息以后,可以确定stable checkpoint
之前的消息在视图切换的过程中不会丢,但是当前检查点之后,下一个检查点之前的已经PREPARE
可能会被丢弃,在视图切换到v+1
后,Pbft
会把旧视图中已经PREPARE
的消息变为PRE-PREPARE
然后新广播。
- 如果集合
P
为空,广播<PRE-PREPARE, v+1, n, null>
,接收节点就什么也不做。 - 如果集合
P
不为空,广播<PRE-PREPARE, v+1, n,d>
总结一下,在view-change
中最为重要的就是C
,P
,Q
三个消息的集合,C
确保了视图变更的时候,stable checkpoint
之前的状态安全。P
确保了视图变更前,已经PREPARE
的消息的安全。Q
确保了视图变更后P
集合中的消息安全。回想一下pre-prepare
和prepare
阶段最重要的任务是保证,同一个主节点
发出的请求在同一个视图(view)
中的顺序是一致的,而在视图切换过程中的C
,P
,Q
三个集合就是解决这个问题的。