分布式算法-Paxos、Raft、ZAB复习
目录
1. Paxos算法
1.1 算法流程
1.2 接受者选举规则
2. Raft算法
2.1 Leader选举
2.2 安全性
3. ZAB算法
3.1 ZAB协议介绍
3.2 消息广播
3.3 崩溃恢复
3.4 数据同步
1. Paxos算法
Paxos 算法是 Leslie Lamport 在 1990 年提出的,经典且完备的分布式一致性算法,该算法的目标是:在不同的节点间,对一个 key 的取值达成共识(同一个值)。
协议将系统中的节点分为三种角色:Proposer(提案者)、Acceptor(决议者)、Leaner(学习者),他们的具体职责为:
- 提案者:对 key 的值提出自己的值;
- 决议者:对提案者的提议进行投票,选定一个提案,形成最终决策;
- 学习者:学习决议者达成共识的决策。
1.1 算法流程
在 Paxos 中,一个决策过程(Round、Phase、Learn)分为三个阶段:
(1)phase1(准备阶段)
- Proposer 向超过半数(n/2+1)Acceptor 发起 prepare 消息(发送编号);
- 如果 Acceptor 收到一个编号为 N 的 Prepare 请求,且 N 大于该 Acceptor 已经响应过的所有 Prepare 请求的编号,那么它就会将它已经接受过的编号最大的提案(如果有的话)作为响应反馈给 Proposer,同时该 Acceptor 承诺不再接受任何编号小于 N 的提案。否则拒绝返回。
(2)phase2(决议阶段或投票阶段)
- 如果超过半数 Acceptor 回复 promise,Proposer 向 Acceptor 发送 accept 消息。注意此时的 V:V 就是收到的响应中编号最大的提案的,如果响应中不包含任何提案,那么 V 就由 Proposer 自己决定;
- Acceptor 检查 accept 消息是否符合规则,只要该 Acceptor 没有对编号大于 N 的 Prepare 请求做出过响应,它就接受该提案。
(3)Learn阶段
- Proposer在收到多数Acceptors的Accept之后,标志着本次Accept成功,决议形成,将形成的决议发送给所有Learners。
1.2 接受者选举规则
Paxos 除了达成共识功能,还实现了容错,在少于一半节点出现故障时,集群也能工作。提案编号大小代表优先级。对于提案编号,接受者提供三个承诺:
- 如果准备请求的提案编号,小于等于接受者已经响应的准备请求的提案编号,那么接受者承诺不响应这个准备请求
- 如果接受请求中的提案编号,小于接受者已经响应的准备请求的提案编号,那么接受者承诺不通过这个提案
- 如果按受者已通过提案,那些接受者承诺会在准备请求的响应中,包含已经通过的最大编号的提案信息
注意:Paxos是一种思想。(个人感觉和Reft算法思想感觉非常相似,就是通过节点间投票选举出包含最新数据的节点作为主节点,然后同步给其他副节点。也可能是我没理解到精髓。)
2. Raft算法
不同于Paxos算法直接从分布式一致性问题出发推导出来,Raft算法则是从多副本状态机的角度提出,用于管理多副本状态机的日志复制。比如Redis集群选主,使用的就是Reft算法。
在 Raft 中,节点被分为 Leader、Follower、Cabdidate 三种角色:
- Leader: 接受客户端请求并向Follower同步请求日志,当日志同步到大多数节点上后告诉Follower提交日志。
- Follower: 接受并持久化Leader同步的日志,在Leader告之日志可以提交之后提交日志。
- Candidate: Leader选举过程中的临时角色。
- Term:是有连续单调递增的编号,每个 term 开始于选举阶段,一般由选举阶段和领导阶段组成。
- 随机超时时间:Follower 节点每次收到 Leader 的心跳请求后,会设置一个随机的,区间位于[150ms, 300ms)的超时时间。若超过超时时间没有收到 Leader 的下一条请求,则认为 Leader 过期/故障了。
- 心跳续命:Leader 在当选期间,会以一定时间间隔向其他节点发送心跳请求,以维护自己Leader地位
2.1 Leader选举
Raft 使用心跳(heartbeat)触发Leader选举。当某个 follower 节点在超时时间内未收到 Leader 的请求,它则认为当前 Leader 宕机或者当前无Leader,将发起选举, 既从一个 Follower 变成 Candidate。过程如下:
- 增加本地节点的Current Term 值
- 状态切换为Candidate
- 该节点投自己一票
- 批量向其他节点发送拉票请求
在这个过程中,其他 Follower 节点收到拉票请求后,需要判断请求的合法性,然后为第一个到达的合法拉票请求进行投票。投票过程对于 Follower 的约束有三点:
- 在一个任期 Term 中只能投一票;
- 候选人的 Term 值大于 Current Term,且候选人已执行的 Log 序号不低于本地 Log,则拉票请求合法;
- 只选择首先到达的合法拉票请求;
有三种选举结果:
- 选举过程收到了过半投票,成为leader
- 选举过程中接收到其他Leader的RPC消息通知,已经有Leader了,直接变为Follower
- 前两种情况均未发生,等待下一轮选举
2.2 安全性
(1)选举安全性:
在一个集群中任何时刻只能有一个 leader,系统中同时有多余一个 leader,被称之为脑裂(brain split),这是非常严重的问题,会导致数据的覆盖丢失。在 raft 中,通过两点保证了这个属性:
- 一个节点某一任期内最多只能投一票;而节点B的term必须比A的新,A才能给B投票。
- 只有获得多数投票的节点才会成为 leader。
(2)日志 append only:
如果两个节点上的某个 log entry 的 log index 相同且 term 相同,那么在该 index 之前的所有 log entry 应该都是相同的。Raft 的日志机制提供两个保证,统称为 Log Matching Property:
- 不同机器的日志中如果有两个entry有相同的偏移和term号,那么它们存储相同的指令。
- 如果不同机器上的日志中有两个相同偏移和 term 号的日志,那么日志中这个 entry 之前的所有 entry 保持一致。
(3)Leader 完备性:
- 被选举人必须比自己知道的更多(比较 term 、log index),选举的安全性其实已经保证了这一点。
3. ZAB算法
3.1 ZAB协议介绍
ZAB 协议全称:Zookeeper Atomic Broadcast(Zookeeper 原子广播协议)。Zookeeper 是一个为分布式应用提供高效且可靠的分布式协调服务,在解决分布式一致性方面,Zookeeper 并没有使用 Paxos ,而是采用了 ZAB 协议。ZAB 协议是为分布式协调服务 Zookeeper 专门设计的一种支持 崩溃恢复 和 原子广播 协议。
基于该协议,Zookeeper 实现了一种 主备模式 的系统架构来保持集群中各个副本之间数据一致性。具体如下图所示:
从上图看,所有客户端写入数据都是写入到 主进程(称为 Leader)中,然后,由 Leader 复制到备份进程(称为 Follower)中。从而保证数据一致性。从设计上看,和 Raft 类似。
Ps:那么复制过程又是如何的呢?
复制过程类似 2PC,ZAB 只需要 Follower 有一半以上返回 Ack 信息就可以执行提交,大大减小了同步阻塞。也提高了可用性。
3.2 消息广播
对于客户端发送的写请求,全部由 Leader 接收,Leader 将请求封装成一个事务 Proposal,将其发送给所有 Follwer ,然后根据所有 Follwer 的反馈,如果超过半数成功响应,则执行 commit 操作(先提交自己,再发送 commit 给所有 Follwer),类似二阶段提交过程。整个广播流程分为 3 步骤:
1. 将数据都复制到 Follwer 中
2. 等待 Follwer 回应 Ack,最低超过半数即成功
3. 当超过半数成功回应,则执行 commit ,同时提交自己
通过以上 3 个步骤,就能够保持集群之间数据的一致性。其他补充须知:
- Leader 在收到客户端请求之后,会将这个请求封装成一个事务,并给这个事务分配一个全局递增的唯一 ID,称为事务ID(ZXID),ZAB 兮协议需要保证事务的顺序,因此必须将每一个事务按照 ZXID 进行先后排序然后处理。
- 在Leader和Follwer之间还有一个消息队列,用来降低他们之间的耦合,解除同步阻塞。
- zookeeper集群中为保证任何所有进程能够有序的顺序执行,只能是 Leader 服务器接受写请求,即使是 Follower 服务器接受到客户端的请求,也会转发到 Leader 服务器进行处理。
- 实际上,这是一种简化版本的 2PC,不能解决单点问题。等会我们会讲述 ZAB 如何解决单点问题(即 Leader 崩溃问题)。
3.3 崩溃恢复
刚刚说消息广播过程中,Leader 崩溃怎么办?还能保证数据一致吗?如果 Leader 先本地提交了,然后 commit 请求没有发送出去,怎么办?ZAB 定义了 2 个原则:
- ZAB 协议确保那些已经在 Leader 提交的事务最终会被所有服务器提交。
- ZAB 协议确保丢弃那些只在 Leader 提出/复制,但没有提交的事务。
基于这两个原则,选举算法:能够确保提交已经被 Leader 提交的事务,同时丢弃已经被跳过的事务。如果让 Leader 选举算法能够保证新选举出来的 Leader 服务器拥有集群总所有机器编号(即 ZXID 最大)的事务,那么就能够保证这个新选举出来的 Leader 一定具有所有已经提交的提案。
这么做有一个好处是:可以省去 Leader 服务器检查事务的提交和丢弃工作的这一步操作。但是,引出新问题,如何同步?
3.4 数据同步
当崩溃恢复之后,需要在正式工作之前(接收客户端请求),Leader 服务器首先确认事务是否都已经被过半的 Follwer 提交了,即是否完成了数据同步,目的是为了保持数据一致。
当所有的 Follwer 服务器都成功同步之后,Leader 会将这些服务器加入到可用服务器列表中。 实际上,Leader 服务器处理或丢弃事务都是依赖着 ZXID 的:
然后基于策略:当 Follower 链接上 Leader 之后,Leader 服务器会根据自己服务器上最后被提交的 ZXID 和 Follower 上的 ZXID 进行比对,比对结果要么回滚,要么和 Leader 同步。