ZAB

  • Post author:
  • Post category:其他


  1. ZAB 协议全称:Zookeeper Atomic Broadcast(Zookeeper 原子广播协议)。

  2. Zookeeper 是一个为分布式应用提供高效且可靠的分布式协调服务。在解决分布式一致性方面,Zookeeper 并没有使用 Paxos ,而是采用了 ZAB 协议。

  3. ZAB 协议定义:

    ZAB 协议是为分布式协调服务 Zookeeper 专门设计的一种支持

    崩溃恢复



    原子广播

    协议

    。下面我们会重点讲这两个东西。

  4. 基于该协议,Zookeeper 实现了一种



    主备模式



    的系统架构来


    保持集群中各个副本之间

    数据一致性



    。具体如下图所示:

image.png

上图显示了 Zookeeper 如何处理集群中的数据。所有客户端写入数据都是写入到 主进程(称为 Leader)中,然后,由 Leader 复制到备份进程(称为 Follower)中。从而保证数据一致性。从设计上看,和 Raft 类似。

  1. 那么复制过程又是如何的呢?复制过程类似 2PC,ZAB 只需要 Follower 有一半以上返回 Ack 信息就可以执行提交,大大减小了同步阻塞(


    与2pc的区别是:2pc1:所有参与者均反馈YES时,即提交事务。2:任何一个参与者反馈NO时,即中断事务。所以2pc要等待所有follower节点反馈,




    而ZAB等待半数节点以上反馈结果即可




    )


    。也提高了可用性。

简单介绍完,开始重点介绍

消息广播



崩溃恢复



整个 Zookeeper 就是在这两个模式之间切换。

简而言之,当 Leader 服务可以正常使用,就进入消息广播模式,当 Leader 不可用时,则进入崩溃恢复模式。

2. 消息广播

ZAB 协议的消息广播过程使用的是一个原子广播协议,类似一个

二阶段提交过程

1:对于客户端发送的写请求,全部由 Leader 接收,Leader 接收到写请求消息请求后,将客户端的写请求消息,赋予一个全局唯一的64位自增id,叫:zxid,通过zxid的大小比较就可以实现因果有序这个特征。Leader 为每个follower准备了一个FIFO队列(通过TCP协议来实现,以实现全局有序这一个特点)将带有zxid的客户端的写请求封装成一个事务 Proposal,作为一个提案(proposal)分发给所有的 follower。

2:当follower接收到proposal,先把proposal写到磁盘,写入成功以后再向leader回复一个ack。

3:如果Leader接收到Follower超过半数的成功ACk响应。

4:则向Follower发送commit消息,执行 commit 操作(先提交自己Leader的事务 Proposal(即执行客户端发送的写请求),再发送 commit 给所有 Follwer,让所有的Follwer也执行commit操作)。


基本上,整个广播流程分为 3 步骤:

1.将数据都复制到 Follwer 中

image.png

image.png

当超过半数的Follower成功回应给Leader Ack响应,那么Leader发送commit消息,让所有的Follower则执行 commit 提交操作

(即执行客户端发送的写数据的请求操作,并提交)

,同时提交自己

(即执行客户端发送的写数据的请求操作,并提交

)

通过以上 3 个步骤,就能够保持集群之间数据的一致性。实际上,在 Leader 和 Follwer 之间还有一个消息队列,用来解耦他们之间的耦合,避免同步,实现异步解耦。


还有一些细节:

  1. Leader 在收到客户端请求之后,会将这个请求封装成一个事务,并给这个事务分配一个全局递增的唯一 ID,称为事务ID(ZXID),ZAB 兮协议需要保证事务的顺序,因此必须将每一个事务按照 ZXID 进行先后排序然后处理。

  2. 在 Leader 和 Follwer 之间还有一个消息队列,用来解耦他们之间的耦合,解除同步阻塞。

  3. zookeeper集群中为保证任何所有进程能够有序的顺序执行,只能是 Leader 服务器接受写请求,即使是 Follower 服务器接受到客户端的请求,也会转发到 Leader 服务器进行处理。

  4. 实际上,这是一种简化版本的 2PC,不能解决单点问题。等会我们会讲述 ZAB 如何解决单点问题(即 Leader 崩溃问题)。

3. 崩溃恢复

刚刚我们说消息广播过程中,Leader 崩溃怎么办?还能保证数据一致吗?如果 Leader 先本地提交了,然后 commit 请求没有发送出去,怎么办?

实际上,当 Leader 崩溃,即进入我们开头所说的崩溃恢复模式(崩溃即:Leader 失去与过半 Follwer 的联系)。下面来详细讲述。



1.ZAB 协议需要确保那些已经在 Leader 服务器上提交的事务最终被所有服务器都提交

假设一个事务在 Leader 服务器上被提交了,并且已经得到过半 Follower 服务器的Ack 反馈,但是在它将 Commit 消息发送给所有 Follower 机器之前, Leader 服务器挂了,针对这种情况, ZAB 协议就需要确保该事务最终能够在所有的服务器上都被提交成功,否则将出现不一致。



2.ZAB协议需要确保丢弃那些只在 Leader 服务器上被提出的事务

假设初始的 Leader 服务器 在提出了一个事务之后就崩溃退出了,导致集群中的其他服务器都没有收到这个事务,当该服务器恢复过来再次加入到集群中的时候 ,ZAB协议需要确保丢弃这个事务。


针对这些问题,ZAB 定义了 2 个原则:

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

所以,ZAB 设计了下面这样一个选举算法:


能够确保提交已经被 Leader 提交的事务,同时丢弃已经被跳过的事务。

针对


ZAB 协议需要确保那些已经在 Leader 服务器上提交的事务最终被所有服务器都提交


这个要求:

如果leader选举算法能够保证新选举出来的Leader服务器,拥有集群中所有机器最高编号(ZXID 最大)的事务Proposal,那么就可以保证这个新选举出来的leader一定具有已经提交的提案。

因为所有提案被commit之前必须有超过半数的follower ack,即必须有超过半数节点的服务器的事务日志上有该提案的proposal,因此只要有合法数量的节点正常工作,就必然有一个节点保存了所有被commit消息的proposal状态。 这样

ZAB 协议

就保证了

那些已经在 Leader 服务器上提交的事务最终被所有服务器都提交



另外一个,zxid是64位,高32位是epoch编号,每经过一次Leader选举产生一个新的leader,新的leader会将epoch号+1,低32位是消息计数器,每接收到一条消息这个值+1,新leader选举后zxid这个值重置为0。这样设计的好处在于老的leader挂了以后重启,它不会被选举为leader,因此此时它的zxid肯定小于当前新的leader。当老的leader作为follower接入新的leader后,新的leader会让它将所有的拥有

旧的epoch号的


未被commit的

proposal

清除

针对


ZAB协议需要确保丢弃那些只在 Leader 服务器上被提出的事务


这个要求:

下面来看 ZAB 协议是如何处理那些需要被丢弃的事务 Proposal 的:

在 ZAB 协议的事务编号 ZXID 设计中, ZXID 是一个 64 位的数字,低 32 位可以看作是一个简单的单调递增的计数器,针对客户端的每一个事务请求, Leader 服务器在产生一个新的事务 Proposal 的时候,都会对该计数器进行加1操作;高 32 位代表了 Leader 周期 epoch 的编号,每当选举产生一个新的 Leader 服务器,就会从这个 Leader 服务器上取出其本地日志中最大事务 Proposal 的 ZXID ,并从该 ZXID 中解析出对应的 epoch 值,然后再对其进行加1操作,之后就会以此编号作为新的 epoch, 并将低 32 位,置为0来开始生成新的 ZXID 。

基于这样的策略,当一个包含了上一个 Leader 周期中尚未提交过的事务 Proposal的服务器启动加入到集群中,发现此时集群中已经存在leader,将自身以Follower 角色连接上 Leader 服务器之后, Leader 服务器会根据自己服务器上最后被提交的 Proposal来和 Follower 服务器的 Proposal进行比对,发现follower中有

上一个leader周期

的事务Proposal时

(上一个leader周期指的是上一个epoch)

,Leader 会要求 Follower 进行一个回退操作(回滚操作)——回退到一个确实已经被集群中过半机器提交的最新的事务 Proposal 。


(改朝换代了,新皇帝登基了,旧的皇帝的指令当然是不好使的了,新的王朝要听新的帝王的指令)

image.png

高 32 位代表了每代 Leader 的唯一性,低 32 代表了每代 Leader 中事务的唯一性。同时,也能让 Follwer 通过高 32 位识别不同的 Leader。简化了数据恢复流程。



基于这样的策略:当 Follower 链接上 Leader 之后,Leader 服务器会根据自己服务器上最后被提交的 ZXID 和 Follower 上的 ZXID 进行比对,比对结果要么回滚,要么和 Leader 同步。

5. 总结





到了总结的时刻了。

ZAB 协议和我们之前看的 Raft 协议实际上是有相似之处的,比如都有一个 Leader,用来保证一致性(Paxos 并没有使用 Leader 机制保证一致性)。再有采取过半即成功的机制保证服务可用(实际上 Paxos 和 Raft 都是这么做的)。

ZAB 让整个 Zookeeper 集群在两个模式之间转换,消息广播和崩溃恢复,消息广播可以说是一个简化版本的 2PC,通过崩溃恢复解决了 2PC 的单点问题,通过队列解决了 2PC 的同步阻塞问题。

而支持崩溃恢复后数据准确性的就是数据同步了,数据同步基于事务的 ZXID 的唯一性来保证。通过 + 1 操作可以辨别事务的先后顺序。



版权声明:本文为luzhensmart原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。