分布式系统的挑战
在前面的文章里,我们分析了分布式系统在业务上的一致性技术,即分布式事务,它的结果导向是面向用户的。然而在我们的系统内部,有时也需要面对来自软件架构等更高层次上的一致性要求,比如 Redis 的哨兵模式,Zookeeper 的选举过程等。它们所考虑的一致性更多的是服务节点之间一个共识
的达成,当共识达成之后,就可以以此为指导原则,展开更多的协同操作。
在研究怎么达成共识之前,我们先来分析下分布式系统的特性:
并发
: 不同节点上的进程是能够同时执行的,我们需要协调机制去完成各个阶段任务。全局时钟
: 在分布式系统里,基本很难去维护一个全局时钟,各个服务器在时间顺序上是没有绝对的。故障影响
: 不存在没有故障的系统,需要考虑对系统的整体影响,以及系统所能提供的容错处理能力。消息传递
:由于网络的复杂环境,节点与节点之间的通信有可能到达,也可能部分到达,可能在已知的时间范围内传送,也可能无限期延迟,这都是不一定的。
由此可见,关于在一个分布式系统里想达成共识的挑战在于协调、容错、非确定通信。
状态复制
如果说我们想在一个系统中引入协调者的话,那么非常简单,引入一个有状态的组件即可,通过状态的判断来保证当前系统应该处于哪个业务阶段。一个有状态的组件是很好实现的,只要带持久化功能即可,像 Mysql,MongoDB。不过,考虑到协调者的重要性,我们往往是需要保证它高可用性的,为了达到这一目的,我们会在状态的更新过程中加入复制流程。比如将更新后的值,同步给其他机器。
但是,是否需要所有的机器都复制到位了,才能完成此次的更新流程?不一定,像 Mysql 同步复制、异步复制、半同步复制就是在性能与数据一致性上给我们提供了多种选择,只是复制的执行效率越高,数据一致性就越低。
像我们这种协调者更新频率低,数据量小,则往往会采用少数服从多数的策略,只要同步节点超过了一半,那么就可以认为此次写入成功了。Raft 的日志同步,Zookeeper 的消息广播就是这么处理的。除此之外,为了保证同步的正确性,还会引入选举机制,让选举出来的 Leader 节点统一处理同步结果。当 Leader 节点故障或下线时,将会根据一定的规则进行重新选举 (比如日志的最新提交程度),保证系统的正常运行。
故障处理
在上面达成共识的方法里,势必要考虑故障的影响,而对应的故障类型主要有两种:
崩溃故障
:节点突然崩溃并停止对其他节点的响应拜占庭失败
:节点是不可信任的,将会响应错误的消息给其他节点
针对于崩溃故障
这种类型的失败,我们可以像 Raft, Paxos 协议一样,通过选举来解决。但是像拜占庭失败
这种问题就比较难解决了,由于有可能存在叛变的节点,使得整个系统往错误的方向去达成共识,显而易见,这不是我们想要的。所以我们会在区块链里看到如下的解决算法:
PBFT(Practical Byzantine Fault Tolerance)
:拜占庭容错算法 (联盟链/私有链使用此算法)PoW(Proof of Work)
:工作量证明算法 (比特币和以太坊使用此算法)
FLP 不可能原理
关于分布式系统之间的通信模型,总体上可以划分下面这两种类型:
- 同步:系统处理消息的时间是在规定范围内的,一旦超出,则直接认为失败。
- 异步:系统处理消息的时间是不定的,有可能获取到结果,也可能一直获取不到了。
其中,在异步
通信模型下,有一个著名的 FLP不可能原理
,即:
在网络可靠、但允许节点失效(即便只有一个)的最小化异步模型系统中,不存在一个可以解决一致性问题的确定性共识算法
FLP 不可能原理告诉我们,不要浪费时间为异步分布式系统设计任意场景的共识算法。我们应该将精力放在一个有约束、有终止条件的分布式系统中,如果我们设计的算法尽可能的满足以下两个条件,那么我们的系统将将会有共识的输出:
- 活性:每个非故障节点最终将会决定输出某个值,如果节点不做决定,那么系统就会停止。
- 安全:所有非故障节点最终将会输出相同的值,如果达不到该效果,那么一致性很难保证。
共识的达成
不同的算法对上面的条件描述会不一样,从广义上来讲,共识算法通常会进行以下三种角色的划分:
提议者
:通常被称为领导者或协调者接受者
:响应提议者提出的议案学习者
:不参与决策,学习决定的最终值
当角色职责划分好后,我们会通过以下三个步骤来定义一个共识算法:
第 1 步 选举: 当有外部事件触发时,由领导者提出下一个有效的输出值。
第 2 步 投票: 非故障节点接收到领导者提议的值后,对其验证,并将其提议为下一个有效值。
第 3 步 决定: 根据有效值在各个非故障节点的提议结果,决定是否采用该值;否则重新开始步骤 1
对于以上的步骤,不同的共识算法会有一些差异,比如术语定义、投票处理流程、有效值的决定标准等。
应用
分布式系统共识的达成需要在不可靠、不可信的网络里完成。如果不进行所谓的拜占庭容错,那么我们的 raft、zookeeper 协议就足够了,而它们的应用场景往往也是在内网之中,所以默认内部节点都是可信的。如果我们要在包含恶意行为的开放的网络群体里达成共识,例如区块链,那么我们就不得不解决考虑以下三种情况的完善了:
- 合理化:参与者根据利益最大化的策略去选择协议的执行。
- 利它式:执行的过程中,能考虑整体的利益。
- 拜占庭式容错:能抵抗某些节点恶意的行为,保证系统正常运行。
总结
分布式系统达成共识的过程需要有活性
和安全
的保障,其协商一致机制也需要将拜占庭错误考虑进去。共识问题的解决让我们的分布式系统运行的更加健壮,也正是因为共识的重要性,当今区块链技术才显得额外的重要!