拜占庭将军问题
拜占庭将军问题是由计算机科学家提出的有关网络通信中一致性的问题。简而言之就是在通信不可靠的情况下怎样保证一致性。
在很久很久以前,拜占庭是东罗马帝国的首都。那个时候罗马帝国国土辽阔,为了防御目的,因此每个军队都分隔很远,将军与将军之间只能靠信使传递消息。在打仗的时候,拜占庭军队内所有将军必需达成一致的共识,才能更好地赢得胜利。但是,在军队内有可能存有叛徒,扰乱将军们的决定。这时候,在已知有成员不可靠的情况下,其余忠诚的将军需要在不受叛徒或间谍的影响下达成一致的协议。
分布式一致性算法
Raft算法
内容
Raft算法是一种简单易懂的共识算法。它依靠状态机和主从同步的方式,在各个节点之间实现数据的一致性。
Raft有两个核心要点,1.选取主节点,2.同步数据。使用主从同步的方式,可以让集群各个节点的数据更新以主节点为准,从而保证了一致性。
选取主节点
Raft算法在选择主节点的过程中,也是通过多个节点之间的投票竞争。Raft算法为节点定义了三种角色:
Leader(主节点)
Follower(从节点)
Candidate(参与投票竞争的节点)
选主的具体流程:
第一步,在最初,还没有一个主节点的时候,所有节点的身份都是Follower。每一个节点都有自己的计时器,当计时达到了超时时间(Election Timeout),该节点会转变为Candidate。
第二步,成为Candidate的节点,会首先给自己投票,然后向集群中其他所有的节点发起请求,要求大家都给自己投票。
第三步,其他收到投票请求且还未投票的Follower节点会向发起者投票,发起者收到反馈通知后,票数增加。
第四步,当得票数超过了集群节点数量的一半,该节点晋升为Leader节点。Leader节点会立刻向其他节点发出通知,告诉大家自己现在是Leader。收到通知的节点全部变为Follower,并且各自的计时器清零。
这里需要说明一点,每个节点的超时时间都是不一样的。比如A节点的超时时间是3秒,B节点的超时时间是5秒,C节点的超时时间是4秒。这样一来,A节点将会最先发起投票请求,而不是所有节点同时发起。
为什么这样设计呢?设想如果所有节点同时发起投票,必然会导致大家的票数差不多,形成僵局,谁也当不成老大。
那么,成为Leader的节点是否就坐稳了老大的位置呢?并不是。Leader节点需要每隔一段时间向集群其他节点发送心跳通知,表明Leader还活着。
一旦Leader节点挂掉,发不出通知,那么计时达到了超时时间的Follower节点会转变为Candidate节点,发起选主投票,重复上面的流程。
同步数据
第一步,由客户端提交数据到Leader节点。
第二步,由Leader节点把数据复制到集群内所有的Follower节点。如果一次复制失败,会不断进行重试。
第三步,Follower节点们接收到复制的数据,会反馈给Leader节点。
第四步,如果Leader节点接收到超过半数的Follower反馈,表明复制成功。于是提交自己的数据,并通知客户端数据提交成功。
第五步,由Leader节点通知集群内所有的Follower节点提交数据,从而完成数据同步流程。
应用场景
用于共享配置和服务发现的K-V存储系统(etcd)中,使用的是Raft算法来保证分布式一致性的。
ZAB 协议
ZAB 协议介绍
ZAB 协议全称:Zookeeper Atomic Broadcast(Zookeeper 原子广播协议)。
Zookeeper 是一个为分布式应用提供高效且可靠的分布式协调服务。在解决分布式一致性方面,Zookeeper 并没有使用 Paxos ,而是采用了 ZAB 协议。
ZAB 协议是为分布式协调服务 Zookeeper 专门设计的一种支持崩溃恢复和原子广播协议。
基于该协议,Zookeeper 实现了一种主备模式的系统架构来保持集群中各个副本之间数据一致性。如下图所示。

上图显示了 Zookeeper 如何处理集群中的数据。所有客户端写入数据都是写入到主进程(称为 Leader)中,然后,由 Leader 复制到备份进程(称为 Follower)中。从而保证数据一致性。从设计上看,和 Raft 类似。
复制过程类似 2PC,ZAB 只需要 Follower 有一半以上返回 Ack 信息就可以执行提交,大大减小了同步阻塞。也提高了可用性。
整个 Zookeeper 就是在消息广播和崩溃恢复这两个模式之间切换。 简而言之,当 Leader 服务可以正常使用,就进入消息广播模式,当 Leader 不可用时,则进入崩溃恢复模式。