Raft算法是一种等价派生于Multi Paxos的分布式共识算法,它源自于题为《In Search of an Understandable Consensus Algorithm》的论文,后来成为Etcd、LogCabin、Consul等重要分布式系统的理论基础。
如何达到分布式共识
Raft算法论文论证了当以下三个问题同时被解决时,即等价于达成共识:
- 如何选主(Leader Election)
- 如何把数据复制到各个节点(Entity Replication)
- 如何保证过程是安全的(Safety)
如何选主
前篇Paxos算法详尽介绍了选主的原理,尽管选主问题的实际工程实践中还涉及很多细节,譬如心跳、随机超时、并行竞选等,其本质还是对分布式系统对“谁当主节点”这件事达成共识,不妨碍我们理解。
如何把数据复制到各个节点
这里我们需要讨论一下数据(Paxos中的提案、Raft中的日志)在各个网络节点间的复制问题:
正常情况
在正常情况下,当客户端向主节点发起一个操作请求,主节点会先写入自己的变更日志,但是不提交,接着把变更信息在下一个心跳包中广播给所有从节点,并要求从节点回复确认收到的消息,从节点收到消息后,将操作写入自己的变更日志,然后给主节点发送确认签收的消息,主节点在收到多数签收消息后,提交自己的变更、应答客户端并且给从节点广播可以提交的消息,从节点收到提交消息后提交自己的变更,此时数据在节点间的复制就完成了。
异常情况
在异常情况下,网络出现分区,部分节点失联,但是只要仍能工作的节点满足过半数的要求,分布式系统就仍然可以正常工作,此时,数据复制过程如下:
假设S1、S2、S3、S4、S5五个节点,S1是主节点,由于网络故障导致S1、S2和S3、S4、S5之间无法通信,形成网络分区。
一段时间后,S3、S4、S5中的某一个节点(假设是S3)先达到心跳超时阈值,此时S3发起竞选主节点广播并收到S4、S5两个节点的批准响应,加上自己的一票达到了多数派的要求,竞选成功,此时系统中存在S1和S3两个主节点,由于网络分区,它们不知道对方的存在。
此时如果客户端发起操作请求会有以下两种情况:
- 如果客户端连接到S1、S2之一,都将由S1处理,由于操作只能获取两个节点的响应,没有达到多数派的要求,所以任何变更都无法更改。
- 如果客户端连接到S3、S4、S5之一,都将由S3处理,此时操作可以满足多数派响应,变更可以被提交,系统可以正常提供服务。
网络分区是由于软、硬件或网络故障构成,内部网络出现分区,但两个分区仍能分别与外部网络中的客户端正常通信的情况非常少见。所以以上两种情况很少能同时出现,更多的场景是算法能容忍网络里下线了部分节点。
假设之后故障恢复,网络分区解除,五个节点恢复通信,此时S1、S3都会向所有节点发送心跳包,从各自的心跳中可以得知S3的任期编号更大,此时五个节点都会承认S3是唯一的主节点,S1、S2会回滚它们所有没有提交的变更,然后从S3的心跳包中获得失联期间所有的变更,将变更写入本地磁盘,此时分布式系统从网络分区中恢复,各个节点的状态达到最终一致。
如何保证过程安全
在分布式理论中有两个描述安全性的预定义术语:
- 协定性(Safety):所有的坏事都不会发生(something “bad” will never happen)
- 终止性(Liveness):所有的好事都终将发生,但不知道是啥时候(something “good” will must happen, but we don’t know when)
术语太过晦涩,以选主问题举例,Safety要保证选主的结果一定是有且只有一个主节点,不可能同时出现两个主节点。Liveness则要保证选举过程一定在某个时刻可以结束。在Liveness整个属性上选主问题存在理论上的瑕疵,即可能由于活锁问题导致无法选出主节点,在Raft论文中只对Safety做了保证,但是由于工程实现上的处理,现实中几乎不会出现终止性问题。