You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

105 lines
10 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 加餐 | PBFT算法如何替换作恶的领导者
你好,我是韩健。
上一讲我们了解到PBFT可以防止备份节点作恶因为这个算法是主节点和备份节点组成的那你想象一下如果主节点作恶比如主节点接收到了客户端的请求但就是默不作声不执行三阶段协议这时无论正常节点数有多少备份节点肯定没办法达成共识整个集群都没办法正常运行。这么大的问题你该怎么解决呢
答案是视图变更View Change也就是通过领导者选举选举出新的主节点并替换掉作恶的主节点。其中的“视图”你可以理解为领导者任期的不同的视图值对应不同的主节点。比如视图值为1时主节点为A视图值为2时主节点为B。
对于领导者模型算法而言不管是非拜占庭容错算法比如Raft还是拜占庭容错算法比如PBFT领导者选举都是它们实现容错能力非常重要的一环。比如对Raft而言领导者选举实现了领导者节点的容错能力避免了因领导者节点故障导致整个集群不可用。而对PBFT而言视图变更除了能解决主节点故障导致的集群不可用之外还能解决主节点是恶意节点的问题。
对你来说理解视图变更可以理解拜占庭容错算法如何处理领导者故障和作恶。这样一样从07讲到13讲非拜占庭容错场景到拜占庭容错场景你就能更全面地理解领导者选举的原理和能解决的问题了这样当你后续熟悉其他领导者选举算法或设计自己的领导者选举算法时也能更加的得心应手了。
既然领导者选举这么重要那么PBFT到底是如何实现视图变更的呢带着这样的疑问我们进入今天的内容。
## 主节点作恶会出现什么问题?
在PBFT中主节点作恶有这么几种情况比如
* 我开篇提到的,主节点接收到客户端请求后,它不做任何处理,也就是默不作声;
* 主节点接收到客户端请求后,给不同的预准备请求分配不同的序号;
* 再或者,主节点只给部分节点发送预准备消息。
需要你注意的是,不管出现哪种情况,共识都是无法达成的,也就是说,**如果恶意节点当选了主节点,此时无论忠诚节点数多少,忠诚节点们将都无法达成共识。**
而这种情况肯定是无法接受的这就需要我们在发现主节点可能在作恶时设计一个机制将作恶的主节点替换掉并保证最终只有忠诚的节点在担任主节点。这样PFBT才能保证当节点数为3f + 1其中f为恶意节点数忠诚的节点们能就客户端提议的指令达成共识并执行一致的指令。
那么在PBFT中视图变更是如何选举出新的主节点并替换掉作恶的主节点的呢答案你肯定知道了那就是视图变更。
## 如何替换作恶的主节点?
在我看来视图变更是保证PBFT算法能稳定运行的关键当系统运行异常时客户端或备份节点触发系统的视图变更通过“轮流上岗”的方式**(v + 1) mod |R|其中v为当前视图的值|R|为节点数**选出下一个视图的主节点,最终选出一个忠诚、稳定运行新主节点,并保证了共识的达成。
为了帮你更好地理解视图变更的原理,我继续以苏秦为例(这次,咱们把叛将楚当作是“大元帅”,让它扮演主节点的角色)。
![](https://static001.geekbang.org/resource/image/d7/d2/d73b976fc3c0d9bc7c1b82d94f11a9d2.jpg "图1")
首先,苏秦联系楚,向楚发送包含作战指令“进攻”的请求。
![](https://static001.geekbang.org/resource/image/f2/be/f22058b2d209978d3488f57375e448be.jpg "图2")
当楚接收到苏秦的请求之后为了达到破坏作战计划的目的它默不作声内心想我就是不执行三阶段协议Three-phase protocol不执行你的指令也不通知其他将军执行你的指令你能把我怎么办
结果苏秦等到花都谢了还是没办法接收到2个相同的响应Reply消息。都过了约定的时间了苏秦在想也许各位将军们出什么问题了。
这时苏秦会直接给各位将军发送作战指令。
![](https://static001.geekbang.org/resource/image/2e/35/2e55e095c723a54d22bb9830f4029435.jpg "图3")
当赵、魏、韩接收到来自的苏秦的作战指令时它们会将作战指令分别发送给楚并等待一段时间如果在这段时间内仍未接收到来自楚的预准备消息那么它们就认为楚可能已经叛变了就发起视图变更采用“轮流上岗”的方式选出新的大元帅比如赵并向集群所有节点发送视图变更消息view-change message
![](https://static001.geekbang.org/resource/image/39/93/398fd2a7b42f79f7bb0f20a2a2d7ba93.jpg "图4")
当赵接收到2个视图变更消息后它就发送新视图消息new-view message给其他将军告诉大家我是大元帅了。
![](https://static001.geekbang.org/resource/image/8e/3c/8ecde229a9c3715346cadc9ff862ce3c.jpg "图5")
当其他将军接收到新视图消息后,就认为选出了新的大元帅。然后,忠诚的将军们就可以一致地执行来自苏秦的作战指令了。
你看,叛变的大元帅,就这样被发现和替换掉了,而最终大元帅一定是忠诚的。
回到计算机的世界中如何理解呢与13讲一样在这里我就不啰嗦了。不过为了帮你更全面地理解视图变更我想补充几点
首先当一个备份节点在定时器超时触发了视图变更后它将暂时停止接收和处理除了检查点CHECKPOINT 、视图变更、新视图之外的消息。你可以这么理解,这个节点认为现在集群处于异常状态,不能再处理客户端请求相关的消息了。
其次,除了演示中的情况,会触发备份节点进行视图变更,下面几种情况也会触发视图变更,比如:
* 备份节点发送了准备消息后在约定的时间内未接收到来自其他节点的2f个相同的准备消息。
* 备份节点发送了提交消息后在约定的时间内未接收到来自其他节点的2f个相同的提交消息。
* 备份节点接收到异常消息,比如视图值、序号和已接受的消息相同,但内容摘要不同。
也就是说,视图变更除了能解决主节点故障和作恶的问题,还能避免备份节点长时间阻塞等待客户端请求被执行。
最后需要你注意的是了解Raft的同学应该知道领导者的选举和日志提交都是由集群的节点来完成的。但在PBFT中客户端参与了拜占庭容错的实现比如客户端实现定时器等待接收来自备份节点的响应并且如果等待超时发送请求给所有节点我希望你能注意到这点。
## 内容小结
本节课我主要带你了解了PBFT是如何替换作恶的领导者的。我希望你明确这样几个重点。
1.客户端通过等待f+1个相同响应消息超时来发现主节点可能在作恶此时客户端发送客户端请求给所有集群节点从而触发可能的视图变更。
2.与Raft在领导者选举期间服务不可用类似在视图变更时PBFT集群也是无法提供服务的。
因为本讲是PBFT算法的最后一讲所以我想多说几句。
首先,在一般情况下,每个节点都需要持久化保存状态数据(比如准备消息),以便在后面使用。但随着系统运行,数据就会越来越多,最终肯定会出现存储空间不足的情况。那么,怎么解决这个问题?
答案是检查点checkpoint机制。PBFT实现了检查点来定时清理节点本地缓存的但已经不再需要的历史数据比如预准备消息、准备消息和提交消息节省了本地的存储空间并不会影响系统的运行。
其次我们都知道基于数字签名的加解密是非常消耗性能这也是为什么在一些对加解密要求高的场景中大家常直接在硬件中实现加解密比如IPSEC VPN。如果在PBFT中所有消息都是签名消息那么肯定非常消耗性能会极大制约PBFT算法的落地场景。那么有什么办法优化这个问题呢
答案是将数字签名和消息验证码MAC混合着使用。具体来说就是在PBFT中只有视图变更消息和新视图消息采用了签名消息其他消息采用的是消息验证码这样一来就节省了大量的加解密的性能开销。
最后PBFT是一个能在实际场景中落地的拜占庭容错算法它和区块链也结合紧密具体来说的话有这么几种应用。
* 相对可信、有许可限制的联盟链比如Hyperledger Sawtooth。
* 与其他拜占庭容错算法结合起来落地公有链。比如Zilliqa将POW算法和PBFT结合起来实现公有链的共识协商。具体来说POW算法作为认证证明节点不是“坏人”PBFT来实现共识。针对PBFT消息数过多、不适应大型分布式系统的痛点Zilliqa实现了分片Sharding技术。
另外也有团队因为PBFT消息数过多、不适应大型分布式系统的痛点放弃使用PBFT通过法律来约束“节点作恶”的行为比如IBM的Hyperledger Fabric。那么我想说的是技术是发展的适合的才是最好的所以我建议你根据场景的可信度来决定是否采用PBFT算法是否改进和优化PBFT算法。
## 课堂思考
既然我提到在PBFT中PBFT是通过视图变更来选举出新的主节点的。那么你不妨想想集群是在视图变更时能否继续处理来自客户端的写请求呢为什么呢欢迎在留言区分享你的看法与我一同讨论。
最后,感谢你的阅读,如果这节课让你有所收获,也欢迎你将它分享给更多的朋友。