天天看点

直观理解:Zookeeper分布式一致性协议ZAB

作者:辣么大的圈圈

ZAB是Zookeeper使用的分布式一致性协议,英文全称是:Zookeeper Atomic Broadcast,因此ZAB也称之为Zookeeper原子广播协议。在解决分布式一致性方面,Zookeeper并没有使用Paxos,而是采用了ZAB协议。基于ZAB协议,Zookeeper实现一种主备模式的系统架构来保持集群中主备副本之间数据的一致性。ZAB协议包括两种基本模式:消息广播(Message Broadcasting)和崩溃恢复(Leader Activation)。下面来详细介绍这两种基本模式的实现过程。

消息广播

消息广播是Zookeeper用来保证写入事务一致性的方法,在Zookeeper集群中,存在以下三种角色的节点:

Leader:Zookeeper集群的核心角色,在集群启动或崩溃恢复中通过Follower参与选举产生,为客户端提供读写服务,并对事务请求进行处理。

Follower:Zookeeper集群的核心角色,在集群启动或崩溃恢复中参加选举,没有被选上就是这个角色,为客户端提供读取服务,也就是处理非事务请求,Follower不能处理事务请求,对于收到的事务请求会转发给Leader。

Observer:观察者角色,不参加选举,为客户端提供读取服务,处理非事务请求,对于收到的事务请求会转发给Leader。使用Observer的目的是为了扩展系统,提高读取性能。

  下面通过几张图对ZAB的消息广播过程进行简单的介绍。

  1. Zookeeper各节点会接收来自客户端的请求,如果是非事务请求,各节点自行进行相应的处理。若接收到的是客户端的事务请求,如果当前节点是Follower则将该请求转发给当前集群中的Leader节点进行处理。
直观理解:Zookeeper分布式一致性协议ZAB
  1. Leader接收到事务处理的请求后,将向所有的Follower节点发出Proposal提议,并等待各Follower的Ack反馈。在广播事务之前Leader服务器会先给这个事务分配一个全局单调递增的唯一ID,也就是事务ID(zxid),每一个事务必须按照zxid的先后顺序进行处理。而且Leader服务器会为每一个Follower分配一个单独的队列,然后将需要广播的事务放到队列中。
直观理解:Zookeeper分布式一致性协议ZAB
  1. 各Follower节点对Leader节点的Proposal进行Ack反馈,Leader对接收到的Ack进行统计,如果超过半数Follower进行了Ack,此时进行下一步操作,否则之间向客户端进行事务请求失败的Response。
直观理解:Zookeeper分布式一致性协议ZAB
  1. 如果Leader节点接收到了超过半数的Ack响应,此时Leader会向所有的Follower发出事务Commit的指令,同时自己也执行一次Commit,并向客户端进行事务请求成功的Response。
直观理解:Zookeeper分布式一致性协议ZAB

  Zookeeper的消息广播过程类似 2PC(Two Phase Commit),ZAB仅需要超过一半以上的Follower返回 Ack 信息就可以执行提交,大大减小了同步阻塞,提高了可用性。

崩溃恢复

在Zookeeper集群启动、运行过程中,如果Leader出现崩溃、网络断开、服务停止或重启等异常情况,或集群中有新服务器加入时,ZAB会让当前集群快速进入崩溃恢复模式并选举出新的Leader节点,在此期间整个集群不对外提供任何读取服务。当产生了新的Leader后并集群中过半Follower完成了与Leader的状态同步,那么ZAB协议就会让Zookeeper集群从崩溃恢复模式转换成消息广播模式。崩溃恢复的目的就是保证当前Zookeeper集群快速选举出一个新的Leader并完成与其他Follower的状态同步,以便尽快进入消息广播模式对外提供服务。

Zookeeper崩溃恢复的主要任务就是选举Leader(Leader Election),Leader选举分两个场景:一个是Zookeeper服务器启动时Leader选举,另一个是Zookeeper集群运行过程中Leader崩溃后的Leader选举。在详细介绍Leader选举过程之前,需要先介绍几个参数:

  • myid: 服务器ID,这个是在安装Zookeeper时配置的,myid越大,该服务器在选举中被选为Leader的优先级会越大。
  • zxid: 事务ID,这个是由Zookeeper集群中的Leader节点进行Proposal时生成的全局唯一的事务ID,由于只有Leader才能进行Proposal,所以这个zxid很容易做到全局唯一且自增。因为Follower没有生成zxid的权限。zxid越大,表示当前节点上提交成功了最新的事务,这也是为什么在崩溃恢复的时候,需要优先考虑zxid的原因。
  • epoch: 投票轮次,每完成一次Leader选举的投票,当前Leader节点的epoch会增加一次。在没有Leader时,本轮此的epoch会保持不变。

另外在选举的过程中,每个节点的当前状态会在以下几种状态之中进行转变。

  • LOOKING: 竞选状态。
  • FOLLOWING: 随从状态,同步Leader 状态,参与Leader选举的投票过程。
  • OBSERVING: 观察状态,同步Leader 状态,不参与Leader选举的投票过程。
  • LEADING: 领导者状态。

集群启动时的Leader选举

  假设现在存在一个由5个Zookeeper服务器组成的集群Sever1,Sever2,Sever3,Sever4和Sever5,集群的myid分别为:1, 2,3,4,5。依次按照myid递增的顺序进行启动。由于刚启动时zxid和epoch都为0,因此Leader选举的关键因素成了myid。

  1. 启动Sever1,此时整个集群中只有Sever1启动,Sever1无法与其他任何服务建立通信,立即进入LOOKING状态,此时Server1给自己投1票(上来都觉得自己可以做Leader),由于1不大于集群总数的一半,即2,此时Sever1保持LOOKING状态。
  2. 启动Sever2,此时Sever2与Server1建立通信,Sever1和Sever2互相交换投票信息,Server1投票的myid为1,Server2投票的myid为2,此时选取myid最大的,因此Sever1的投票会变成2,但是由于目前投票Server2的服务器数量为2台,小于集群总数的一半2,因此Sever1和Sever2继续保持LOOKING状态。
  3. 启动Sever3,此时三台服务器之间建立了通信,Server3进入LOOKING状态,并与前两台服务器交换投票信息,Server1和Server2的投票信息为2,Server3投票自己,即myid为3,这个时候选择myid最大的作为Leader。此时集群中投票3的服务器数量变成了3台,此时3>2,Sever3立刻变成LEADING状态,Sever1和Sever2变成FOLLOWING状态。
  4. 启动Sever4,Sever4进入LOOKING状态并与前三台服务器建立通信,由于集群中已经存在LEADING状态的节点,因此,Sever4立刻变为FOLLOWING状态,此时Sever3依旧处于LEADING状态。

    5.动Sever5,Sever5与Sever4一样,在与其他服务器建立通信后会立刻变为FOLLOWING状态,此时Sever3依旧处于LEADING状态。

    最终整个Zookeeper集群中,Server3成为Leader,Server1,Server2,Server4和Server5成为Follower,最终Server3的epoch加一。

Leader崩溃时的Leader选举

  在Zookeeper集群刚启动的时候,zxid和epoch并不参与群首选举。但是如果Zookeeper集群在运行了一段时间之后崩溃了,那么epoch和zxid在Leader选举中的重要性将大于myid。重要性的排序为:epoch>zxid>myid。当某一个Follower与Leader失去通信的时候,就会进入Leader选举,此时Follower会跟集群中的其他节点进行通信,但此时会存在两种情况:

  1. Follower与Leader失去通信,但此时集群中的Follower并未崩溃,且与其他Follower保持正常通信。此时当该Follower与其他Follower进行通信时,其他Follower会告诉他,老大还活着,这个时候,Follower仅需要与Leader建立通信即可。
  2. Leader真的崩溃了,此时集群中所有节点之间会进行通信,当得知老大挂了之后,每个节点都会开启争老大模式,各自会将当前节点最新的epoch,zxid和myid发送出来,参与投票,此时各节点之间会参照epoch>zxid>myid进行Leader选举,最后投票数超过集群数量一般的节点会成为新的Leader。

  这种崩溃后的Leader选举机制也很好理解,如果Leader挂了,优先选择集群中最后做过(epoch)Leader的节点为新的Leader节点,其次选取有最新事务提交的节点(zxid)为Leader,最后才按默认的最大机器编号(myid)进行投票。