ZAB协议

介绍

  ZAB协议的全称是Zookeeper Atomic Broadcast(Zookeeper)原子广播。
  Zookeeper是通过 Zab 协议来保证分布式事务的最终一致性。

  1. ZAB协议是为分布式协调服务Zookeeper专门设计的一种支持崩溃恢复原子广播协议,是Zookeeper保证数据一致性的核心算法。Zab借鉴了Paxos算法,但又不想Paxos是一种通用的分布式一致性算法。他是为Zookeeper专门设计的。
  2. 在Zookeeper中主要依赖ZAB协议来实现数据一致性,基于该协议,zk实现了一种主备模型的系统架构来保证集群中各个副本之间的数据一致性。
  3. Zookeeper使用一个单一的主进程来接收并处理客户端的所有事务请求,并通过ZAB协议,将服务器数据的状态变更以事务Proposal的形式广播到所有副本进程上去。ZAB协议的这个主备架构模型保证了同一时刻集群中只能有一个主进程来广播服务器的状态变更,因此能够很好地处理客户端的并发请求。
  4. ZAB协议通过一个**全局递增的事务id(Zxid)**,来保证状态变更的顺序性,也就是说,ZAB保证了一个状态变更的请求如果已经被处理,那么所有该变更所依赖的状态变更都已经被处理过了。创建节点的顺序,/a与/a/b。
  5. 考虑到主进程在任何时刻都可能出现宕机的情况,ZAB协议还保证了即使主进程出现异常,只要集群中有半数以上节点存活,就仍然可以正常提供服务。

特性

  1. ZAB协议需要确保那些已经在Leader服务器上提交的事务最终被所有的服务器提交。
  2. ZAB协议需要确保丢弃那些只在Leader上被提出而没有被提交的事务。
  3. ZAB算法设计为新被选举出来的Leader拥有集群中ZXID最大的事务Proposal。这样就可以保证新的Leader一定具有所有已经提交的Proposal。更为重要的是,如果让ZXID最大的节点成为Leader,就可以省去Leader节点检查Proposal的提交和丢弃的工作了。

核心

  ZAB协议的核心是,定义了如何处理那些会改变Zookeeper服务器数据状态的事务请求。

  1. 所有的事务请求必须由一个全局唯一的服务器来协调处理,这样的服务器被叫做Leader服务器。其他剩余的服务器则是Follower服务器。
  2. Leader服务器负责将一个客户端事务请求,转换成一个事务Proposal,并将该Proposal分发给集群中所有的Follower服务器,也就是向所有Follower节点发送数据广播请求(或数据复制)。
  3. 分发之后Leader服务器需要等待所有Follower服务器的反馈(Ack请求),在Zab协议中,只要超过半数的Follower服务器进行了正确的反馈后(也就是收到半数以上的Follower的Ack请求),那么Leader就会再次向所有的Follower服务器发送Commit消息,要求其将上一个事务Proposal进行提交。

内容

  ZAB协议包含两种基本的模式:崩溃恢复消息广播

  协议过程

  1. 当整个集群启动过程中,或者当 Leader 服务器出现网络中弄断、崩溃退出或重启等异常时,Zab协议就会进入崩溃恢复模式,选举产生新的Leader。
  2. 当选举产生了新的Leader,同时集群中有过半的机器与该Leader服务器完成了状态同步(即数据同步)之后,Zab协议就会退出崩溃恢复模式,进入消息广播模式。
  3. 这时,如果有一台遵守Zab协议的服务器加入集群,因为此时集群中已经存在一个Leader服务器在广播消息,那么该新加入的服务器自动进入恢复模式:找到Leader服务器,并且完成数据同步。同步完成后,作为新的Follower一起参与到消息广播流程中。

  协议状态切换

  当Leader出现崩溃退出或者机器重启,亦或是集群中不存在超过半数的服务器与Leader保存正常通信,Zab就会再一次进入崩溃恢复,发起新一轮Leader选举并实现数据同步。同步完成后又会进入消息广播模式,接收事务请求。

  保证消息有序

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

崩溃恢复

  当Zookeeper集群初始化时,或Leader故障宕机时,ZAB协议就会进入崩溃恢复模式,并选举出新的Leader。当新的Leader选举出来后,并且集群中已经有过半的节点与Leader完成了数据同步,ZAB协议就会退出崩溃恢复模式,转而进入消息广播模式。一个节点要想成为Leader,必须获得集群中过半节点的支持。
  在ZAB协议中,为了保证程序的正确运行,整个恢复过程结束后需要选举出一个新的Leader服务器。因此ZAB协议需要一个高效且可靠的Leader选举算法,从而确保能够快速选举出新的Leader。
  Leader选举算法不仅仅需要让Leader自己知道自己已经被选举为Leader ,同时还需要让集群中的所有其他机器也能够快速感知到选举产生的新Leader服务器。

  当新的机器加入到集群中的时候,如果已经存在leader服务器,那么新加入的服务器就会自觉进入崩溃恢复模式,找到leader进行数据同步。
  崩溃恢复主要包括两部分:Leader选举和数据恢复。

消息广播

  ZAB的正常工作模式。

  Zookeeper设计成只允许唯一的一个Leader节点负责处理客户端的事务请求,当Leader接收到事务请求后,会生成相应的事务Proposal并发起一轮消息广播。如果集群中的非Leader节点(Follower或Observer)接收到了事务请求,会将请求转发给Leader处理。当Leader宕机,或者是集群中已经不存在超过半数的节点与Leader保持正常通信,那么集群就会进入崩溃恢复模式。

  在zookeeper集群中,数据副本的传递策略就是采用消息广播模式。
  ZAB协议的消息广播模式采用的是原子消息广播,类似于一个两阶段提交,Leader接收客户端的事务请求,为其生成对应的Proposal,并广播给集群中所有其他的服务器,然后分别收集每个服务器的选票,最后进行事务提交。

  Zookeeper中农数据副本的同步方式与二段提交相似,但是却又不同。二段提交要求协调者必须等到所有的参与者全部反馈ACK确认消息后,再发送commit消息。要求所有的参与者要么全部成功,要么全部失败。二段提交会产生严重的阻塞问题。
  Zab协议中 Leader 等待 Follower 的ACK反馈消息是指“只要半数以上的Follower成功反馈即可,不需要收到全部Follower反馈”。

   在整个消息广播的过程中,Leader会为每个事务请求生成Proposal并进行广播。此外,在广播Proposal之前,Leader会首先为这个Proposal生成全局单调递增的唯一ID,称为事务ID,也即ZXID。ZAB协议会严格按照ZXID的顺序处理每个Proposal,保证了消息的顺序性。
  在消息广播过程中,Leader会在Leader侧为每个Follower都各自分配一个单独的队列,然后将需要广播的Proposal依次放入这些队列中,按照FIFO的原则进行发送。Follower在接收到Proposal后,会首先将其以事务日志的形式写入磁盘中,写入成功后给Leader响应ACK。当Leader收到超过半数的Follower的ACK后,会广播一个Commit消息通知所有Follower进行事务提交,同时Leader自身也会提交事务。Follower在收到Commit消息后,就会完成对事务的提交。

  Zookeeper采用Zab协议的核心,就是只要有一台服务器提交了Proposal,就要确保所有的服务器最终都能正确提交Proposal。这也是CAP/BASE实现最终一致性的一个体现。
  Leader服务器与每一个Follower服务器之间都维护了一个单独的FIFO消息队列进行收发消息,使用队列消息可以做到异步解耦。 Leader和Follower之间只需要往队列中发消息即可。如果使用同步的方式会引起阻塞,性能要下降很多。

  消息广播过程:

  1. 客户端发起一个写请求。
  2. Leader服务器将客户端的请求转化为事务Proposal提案,同时为每个Proposal分配一个全局的ID,即Zxid。
  3. Leader服务器为每个Follower服务器分配一个单独的队列,然后将需要广播的Proposal依次放到队列中取,并且根据FIFO策略进行消息发送。
  4. Follower接收到Proposal后,会首先将其以事务日志的方式写入本地磁盘中,写入成功后向Leader反馈一个Ack响应消息。
  5. Leader接收到超过半数以上Follower的Ack响应消息后,即认为消息发送成功,可以发送commit消息。
  6. Leader向所有Follower广播commit消息,同时自身也会完成事务提交。Follower接收到commit消息后,会将上一条事务提交。

原理

  ZAB协议要求每个Leader都要经历三个阶段:发现,同步,广播。

  1. 发现:要求Zookeeper集群必须选举出一个Leader进程,同时Leader会维护一个Follower可用列表。将来客户端可以和Follower节点进行通信。
  2. 同步:Leader要负责将本身的数据与Follower完成同步,做到多副本存储。这样也是提现了CAP中的高可用和分区容错。Follower将队列中未处理完的请求消费完成后,写入本地事务日志中。
  3. 广播:Leader可以接受客户端新的事务Proposal请求,将新的Proposal请求广播给所有的Follower。

   在正常运行的情况下,ZAB协议会一直处于阶段三来反复地进行消息广播流程。如果出现Leader崩溃或者其他原因导致Leader缺失,ZAB协议就会再次进入阶段一,重新选举新的Leader。


实现

  Zookeeper节点有三种状态:

  1. Following:当前节点是跟随着,服从Leader的命令。
  2. Leading:当前节点是Leader,负责协调事务。
  3. Looking:节点处于选举状态,正在寻找Leader。

   当ZAB协议的进程刚开始启动时,所有进程都处于Looking的初始化状态,此时集群中并不存在Leader。接下来,所有处于Looking状态的进程都会试图去选举出一个Leader。如果某个进程发现已经选举出了Leader,那么它会马上切换到Following,开始和Leader保存同步。此时,我们将处于Following状态的进程称为Follower,处于Leading状态的进程称为Leader。
  考虑到Leader进程随时可能挂掉,当检测出Leader已经崩溃或放弃领导地位时,其余的Following状态的进程就会重新进入Looking状态,并开始进行新一轮的Leader选举。因此在ZAB协议中,每个进程的状态都在Looking、Following和Leading之间不断转换。
  在进程完成Leader选举和数据同步之后,ZAB协议就进入了广播阶段。在这个阶段中,Leader会为每一个与自己保持同步的Follower创建一个操作队列。同一时刻,一个Follower只能与一个Leader保持同步。
  Leader进程与所有的Follower进程之间通过心跳检测机制来感知彼此的状态。如果Leader能在超时时间内收到Follower的心跳,则Follower就会一直与Leader保持同步。而一旦在超时时间内Leader无法收到过半的Follower的心跳信息,或者TCP连接本身断开了,那么Leader就会停止对当前周期的领导,并转换到Looking状态。同时,所有Follower也会放弃这个Leader,进入Looking状态。之后,所有进程就会开启新一轮的Leader选举。

Zab协议详解