Zookeeper 的 ZAB 协议

Zookeeper 的 ZAB 协议


目录

  1. ZAB 协议
  2. ZAB 协议介绍
  3. ZAB 协议内部原理
  4. ZAB 与 Paxos 算法的联系与区别

1. ZAB 协议

ZAB 协议并不像 Paxos 算法那样,是一种通用的分布式一致性算法,它是一种特别为 Zookeeper 设计的崩溃可恢复的原子广播算法。

在 Zookeeper 中,主要依赖 ZAB 协议来实现分布式数据一致性,基于该协议,Zookeeper 实现一种主备模式的系统架构来保持集群中各副本之间数据的一致性。具体的,Zookeeper 使用一个单一的主进程来接收并处理客户端的所有事务请求,并采用 ZAB 是原子广播协议,将服务器数据的状态变更以事务 Proposal 的形式广播到所有的副本进程上去。ZAB 协议的这个主备模型架构保证了同一时刻集群中只能够有一个主进程来广播服务器的状态变更,因此能够很好地处理客户端大量的并发请求。另一方面,顺序执行的一些状态变更其前后会存在一定的依赖关系,ZAB 协议必须能够保证一个全局的变更序列被顺序应用,也就是说,ZAB 协议需要保证一个状态变更已经被处理了,那么其所依赖的状态变更应该已经提前处理掉了。最后,考虑到主进程在任何时候都有可能出现崩溃退出或重启现象,因此,ZAB 协议还需要做到在当前主进程出现上述异常情况的时候,依然能够正常工作。

ZAB 协议的核心是定义了对于那些会改变 Zookeeper 服务器数据状态的事务请求的处理方式,即:
所有事务请求必须由一个全局唯一的服务器来协调处理,这样的服务器被称为 Leader 服务器,而余下的其他服务器则成为 Follower 服务器。Leader 服务器负责将一个客户端事务请求转换成一个事务 Proposal(提议),并将该 Proposal 分发给集群中所有的 Follower 服务器。之后 Leader 服务器需要等待所有 Follower 服务器的反馈,一旦超过半数的 Follower 服务器进行了正确的反馈后,Leader 就会再次向所有的 Follower 服务器分发 Commit 消息,要求其将前一个 Proposal 进行提交。


2. ZAB 协议介绍

ZAB 协议包括两种基本模式,分别是崩溃恢复和消息广播。当整个服务框架在启动过程中,或是当 Leader 服务器出现网络中断、崩溃退出与重启等异常情况时,ZAB 协议就会进入恢复模式并选举产生新的 Leader 服务器。当选举产生新的 Leader 服务器,同时集群中已经有过半的机器与该 Leader 服务器完成了状态同步之后,ZAB 协议就会退出恢复模式。其中,所谓的状态同步是指数据同步,用来保证集群中存在过半的机器能够和 Leader 服务器的数据状态保持一致。

当集群中已经有过半的 Follower 服务器完成了和 Leader 服务器状态同步,那么整个服务框架就可以进入消息广播模式了。当一台同样遵循 ZAB 协议的服务器启动后加入到集群中,如果此时集群中已经有一个 Leader 服务器在负责进行消息广播,那么新加入的服务器就会自觉进入数据恢复模式:找到 Leader 所在的服务器,并与其进行数据同步,然后一起参加到消息广播流程中去。 Zookeeper 设计成只允许唯一的一个 Leader 服务器来进行事务请求的处理。Leader 服务器在接收到客户端的事务请求后,会生成对应的事务请提案并发起一轮广播协议;而如果集群中的其他机器接收到客户端的事务请求,那么这些非 Leader 服务器首先将这个事务请求转发给 Leader 服务器。

当 Leader 服务器出现崩溃退出或机器重启,亦或是集群中已经不存在过半的服务器与该 Leader 服务器保持正常通信时,那么在重新开始新一轮的原子广播事务操作之前,所有进程首先会使用崩溃恢复协议来使彼此达到一个一致的状态,于是整个 ZAB 流程就会从消息广播模式进入到崩溃恢复模式。

一个机器要成为新的 Leader,必须获得过半进程的支持,同时由于每个进程都有会崩溃,因此 ZAB 协议运行过程中,前后出现多个 Leader,并且每个进程也有可能会多次成为 Leader。进入崩溃恢复模式后,只要集群中存在过半的服务器能够彼此进行正常通信,那么就可以产生一个新的 Leader 并在此进入消息广播模式。举个例子来说,一个由 3 台机器组成的 ZAB 服务,通常由 1 个 Leader,2个 Follower 服务器组成。某一个时刻,假如其中一个 Follower 服务器挂了,整个 ZAB 机器是不会中断服务的,这是因为 Leader 服务器依然能够获得过半机器(包括 Leader 自己)的支持。

接下来重点介绍 ZAB 协议的消息广播和崩溃恢复过程。


消息广播

ZAB 协议的消息广播过程使用的是一个原子广播协议,类似于一个二阶段提交过程。针对客户端的事务请求,Leader 服务器会为其生产对应的事务 Proposal,并将其发送给集群中其余的机器,然后再分别收集各自的选票,最后进行事务提交,如下图就是 ZAB 协议消息广播流程的示意图。
在这里插入图片描述

此处的 ZAB 协议中涉及的二阶段提交过程与二阶段提交协议略有不同。在 ZAB 协议的二阶段提交过程中,移除了中断逻辑,所有的 Follower 服务器要么正常反馈 Leader 提出的事务 Proposal,要么抛弃 Leader 服务器。同时,ZAB 协议将二阶段提交中的中断逻辑移除意味着我们可以在过半的 Follower 服务器已经反馈 Ack 之后就可以开始提交事务 Proposal了,而不需要等待集群中所有的 Follower 服务器都反馈响应。当然,在这种简化的二阶段提交模型下,是无法处理 Leader 服务器崩溃退出而带来的数据不一致问题的,因此在 ZAB 协议中添加了另一个模式,即采用崩溃恢复模式来解决这个问题。另外,整个消息广播协议是基于具有 FIFO 特性的 TCP 协议来进行网络通信的,因此能够很容易地保证广播过程中消息接收与发送的顺序性。

在整个消息广播过程中,Leader 服务器会为每个事务请求生成对应的 Proposal 来进行广播,并且在广播事务 Proposal 之前,Leader 服务器会首先为这个事务 Proposal 分配一个全局单调递增的唯一 ID,我们称之为事务 ID(ZXID)。由于 ZAB 协议需要保证每一个消息严格的因果关系,因此必须将每一个事务 Proposal 按照其 ZXID 的先后顺序来进行排序与处理。

具体的,在消息广播过程中,Leader 服务器会为每一个 Follower 服务器都各自分配一个单独的队列,然后将需要广播的事务 Proposal 依次放入这些队列中去,并根据 FIFO 策略进行消息发送。每一个 Follower 服务器在接收到这个事务 Proposal 之后,都会首先将其以事务日志的形式写入到本地磁盘中去,并且在成功写入后反馈给 Leader 服务器一个 Ack 响应。当 Leader 服务器接收到超过半数 Follower 的 Ack 响应后,就会广播一个 Commit 消息给所有的 Follower 服务器以通知其进行事务提交,同时 Leader 自身也会完成对事务的提交,而每一个 Follower 服务器在接收到 Commit 消息后,也会完成对事务的提交。


崩溃恢复

ZAB 协议的这个基于原子广播协议的消息广播过程,在正常情况下运行非常良好,但是一旦 Leader 服务器出现崩溃,或者说由于网络原因导致 Leader 服务器失去了与过半 Follower 的联系,那么就会进入崩溃恢复模式。在 ZAB 协议中,为了保证程序正确运行,整个恢复过程结束后需要选举初一个新的 Leader 服务器。因此 ZAB 协议需要一个高效且可靠的 Leader 选举算法,从而确保能够快速选举出新的 Leader。同时 Leader 选举算法不仅仅让 Leader 自己知道自身已经被选举为 Leader,同时还需要让集群中所有的其他机器也能够快速地感知到选举产生的新的 Leader 服务器。

基本特性

ZAB 协议规定如果一个事务 Proposal 在一台机器上被处理成功,那么应该在所有的机器上都被处理成功,哪怕机器出现故障崩溃。接下来我们看看在崩溃恢复过程中,可能会出现的两个数据不一致的隐患及针对这些情况 ZAB 协议所需要保证的特性。

ZAB 协议确保那些已经在 Leader 提交的事务最终会被所有服务器提交。
ZAB 协议确保丢弃那些只在 Leader 提出/复制,但没有提交的事务。

如果在崩溃恢复过程中出现一个需要被丢弃的提案,那么在崩溃恢复结束后需要跳过该事务 Proposal,如下图所示

在这里插入图片描述
在上图所示的集群中,假设初始的 Leader 服务器 Server1 在提出一个事务 Proposal3 之后就崩溃退出了,从而导致集群中的其他服务器都没有收到这个事务 Proposal。于是,当 Server1 恢复过来再次加入集群中的时候,ZAB 协议需要确保丢弃 Proposal3 这个事务。

结合上面提到的这两个崩溃恢复过程中需要处理的特殊情况,就决定了 ZAB 协议必须设计这一一个 Leader 选举算法:能够确保提交已经被 Leader 提交的事务 Proposal,同时丢弃已经被跳过的事务 Proposal。针对这个要求,如果让 Leader 选举算法能够保证新选举出来的 Leader 服务器拥有集群中所有机器最高编号(即 ZXID 最大)的事务 Proposal,那么就可以保证这个新选举出来的 Leader 一定具有所有已经提交的提案。更为重要的是,如果让具有最高编号事务 Proposal 的机器来成为 Leader,就可以省去 Leader 服务器检查 Proposal 的提交和丢弃工作的这个步操作了。


数据同步

完成 Leader 选举之后,在正式开始工作(即接收客户端的事务请求,然后提出新的提案)之前,Leader 服务器会首先确认事务日志中的所有 Proposal 是否都已经被集群中过半的机器提交了,即是否完成数据同步。下面来看看 ZAB 协议的数据同步过程。

所有正常运行的服务器,要么成为 Leader,要么成为 Follower 并和 Leader 保持同步。Leader 服务器需要确保所有的 Follower 服务器能够接收到每一条事务 Proposal,并且能够正确地将所有已经提交了的事务 Proposal 应用到内存数据库中。具体的,Leader 服务器会为每一个 Follower 服务器都准备一个队列,并将那些没有被各 Follower 服务器同步的事务以 Proposal 消息的形式逐个发送给 Follower 服务器,并在每一个 Proposal 消息后面紧接着 Commit 消息,以表示该事务已经被提交。等到 Follower 服务器将所有其尚未同步的事务 Proposal 都从 Leader 服务器上同步过来并成功应用到本地数据库中后,Leader 服务器就会将该 Follower 服务器加入到真正的可用 Follower列表中,并开始之后的其他流程。

上面讲到的是正常情况下的数据同步逻辑,下面来看 ZAB 协议是如何处理那些需要被丢弃的事务 Proposal 的。在 ZAB 协议的事务编号 ZXID 设计中,ZXID 是一个 64 位的数字,其中低 32 位可以看做是一个简单的单调递增的计数器,针对客户端的每一个事务请求, Leader 服务器在产生一个新的事务 Proposal 的时候,都会对该计数器进行加 1 操作;而高 32 位则代表了 Leader 周期的 epoch 编号每当选举产生一个新的 Leader 服务器,就会从这个 Leader 服务器上取出其本地日志中最大事务 Proposal 的 ZXID,并从该 ZXID 中解析出对应的 epoch 值,然后再对其进行加 1 操作,之后就会以此编号作为新的 epoch,并将低 32 位从位置 0 来开始生成新的 ZXID。ZAB 协议中的这一通过 epoch 编号来区别 Leader 周期编号的策略,能够有效地避免不同 Leader 服务器错误地使用相同的 ZXID 编号提出不一样的事务 Proposal 的异常情况,这对于识别在 Leader 崩溃恢复前后生成的 Proposal 非常有帮助,大大简化和提升了数据恢复流程。

基于这样的策略,当一个包含了上一个 Leader 周期中尚未提交过的事务 Proposal 的服务器启动时,其肯定无法成为 Leader,原因很简单,因为当前集群中一定包含一个 Quorum 集合,该集合中的机器一定包含了更高 epoch 事务 Proposal,因此这台机器的事务 Proposal 肯定不是最高,也就无法成为 Leader。当这台机器加入集群中,以 Follower 角色连接上 Leader 服务器之后,Leader 服务器会根据自己服务器上最后被提交的 Proposal 来和 Follower 服务器的 Proposal 进行比对,比对结果当然是 Leader 会要求 Follower 进行一个回退操作——回退到一个确实已经被集群过半机器提交的最新的事务 Proposal。举个例子来说,在上图,当 Server1 连接上 Leader 后,Leader 要求 Server1 去除 P3。


3. ZAB 协议内部原理

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

运行分析

在 ZAB 协议的设计中,每一个进程都有可能处于以下三种状态之一。

  • LOOKING:Leader 选举阶段
  • FOLLOWING:Follower 服务器和 Leader 保证同步状态
  • LEADING:Leader 服务器作为主进程领到状态。

组成 ZAB 协议的所有进程启动时,其初始状态都是 LOOKING 状态,此时进程组中不存在 Leader。所有处于这种状态的进程,都会试图去选举出一个新的 Leader。随后,如果进程发现已经选举出新的 Leader,那么它就会马上切换到 FOLLOWING 状态,并开始和 Leader 保持同步。这里,我们将处于 FOLLOWING 状态的进程称为 Follower,将处于 LEADING 状态的进程称为 Leader。考虑到 Leader 随时会挂掉,当检测出 Leader 崩溃或者放弃了领到地位时,其余的 Follower 进程就会转换到 LOOKING 状态,并开始新一轮的 Leader 选举。因此在 ZAB 协议运行过程中,每个进程都会在 LEADING,FOLLOWING 和 LOOKING 状态之间切换。

Leader 选举过程发生在前两个阶段。图 4-5 展示了一次 Leader 选举过程中,各进程之间的消息发送与接收情况。需要注意的是,只有在完成了阶段二,即完成各进程之间的数据同步之后,准 Leader 进程才能真正成为新的主进程周期中的 Leader。具体的,我们将一个可用的 Leader 定义如下:
如果一个准 Leader L 接收到来自过半的 Follower 进程针对 L 的 NEWLEADER(e,I)
完成 Leader 选举以及数据同步之后,ZAB 协议就进入原子广播阶段,在这一阶段中,Leader 会以队列的形式为每一个与自己保持同步的 Follower 创建一个操作队列。同一时刻,一个 Follower 只能和一个 Leader 保持同步,Leader 进程与所有的 Follower 进程之间都通过心跳机制来感知彼此的情况。如果 Leader 能够在超时时间内正常收到心跳检测,那么 Follower 就会一直与该 Leader 保持连接。而如果指定的超时时间内 Leader 无法从过半的 Follower 进程那接收到心跳检测,或者是 TCP 连接本身断了,那么 Leader 就会终止对当前周期的领到,并切换到 LOOKING 状态,所有的 Follower 也会选择放弃这个 Leader,同时转换到 LOOKING 状态。之后,所有的进程就会开始新一轮的 Leader 选举,并在选举新的 Leader 之后开始新一轮的主进程周期。


4. ZAB 与 Paxos 算法的联系与区别

ZAB 协议并不是 Paxos 算法的一个经典实现,在讲解 ZAB 和 Paxos 之间的区别之前,首先来看下两种的联系。

  • 两者都存在一个类似于 Leader 进程的角色,由其负责协调多个 Follower 进程运行。
  • Leader 进程都会等待超过半数的 Follower 做出正确的反馈后,才会将一个提案进程提交。
  • 在 ZAB 协议中,每个 Proposal 中都包含了一个 epoch值,用爱来代表当前 Leader 周期,在 Paxos算法中,同样存在这样的一个标识,只是名字变成了 Ballot。

在 Paxos 算法中,一个新选举产生的主进程会进行两个阶段的工作。第一阶段被称为读阶段,在这个阶段,这个新的主进程会通过和所有的其他进程进行通信的方式来收集上一个主进程提出的提案,并将它们提交。第二阶段称为写阶段,在这个阶段,当前主进程开始提出自己的提案。在 Paxos 算法设计的基础上,ZAB 协议额外添加了一个同步阶段,ZAB 协议也存在一个和 Paxos 算法中的读阶段非常类似的过程,称为发现(Discovery)阶段。在同步阶段,新的 Leader 会确保存在过半的 Follower 已经提交了之前 Leader 周期中所有事物 Proposal。这一同步阶段的引入,能够有效地保证 Leader 在新的周期中提出事物 Proposal 之前,所有的进程都已经完成了对之前所有事物 Proposal 的提交。一旦完成同步阶段后,那么 ZAB 就会执行和 Paxos 算法类似的写阶段。

总是来讲,ZAB 协议和 Paxos 算法的本质区别在于,两者的设计目标不太一样。ZAB 协议主要用来构建一个高可用的分布式数据主备系统,例如 Zookeeper,而 Paxos 算法则用来构建一个分布式的一致性状态机系统。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值