天天看点

ZooKeeper系列:ZAB协议一、ZAB协议的核心二、基本模式

一、ZAB协议的核心

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

    这里需要注意的是,如果是集群中的非Leader服务器收到了客户端的事务请求,那么会将这个请求转发给Leader服务器。

二、基本模式

    ZAB协议主要包含两个基本模式:消息广播和崩溃恢复。

1、消息广播

    当集群中过半服务器和Leader完成了状态同步后,ZAB协议就进入了消息广播模式。

    ZAB协议通过原子广播协议来实现消息的广播过程。当Leader服务器接收到客户端的事务请求时,会为其生成对应的事务Proposal,并将Proposal发送给集群中的其他机器。Follower收到Proposal后有两个选择:同意事务,或者抛弃Leader服务器。当Leader服务器收集到超过一半Follower的投票时,就会想所有的机器发送Commit命令,要求提交事务。

    当然,这里有一些问题。如果Leader在发送完全部Commit命令前就发生了故障,这时候可能会导致这次的数据更新产生不一致的问题,因为可能有些服务器上提交了事务,而有些服务器没收到事务提交的请求。zk通过使用崩溃恢复机制来解决这个问题。

    另外,消息广播使用TCP协议进行通信。TCP协议FIFO的特性可以保证消息广播过程中消息接收预发送的顺序性。

    对于广播事务Proposal,Leader服务器会为每个事务请求都分配一个全局唯一、且单调递增的事务id(ZXID)。

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

2、崩溃恢复

    在集群启动,或者Leader服务器出现网络中断、崩溃退出、重启等异常情况,导致Leader服务器不能和过半服务器取得联系的时候,ZAB协议会进入恢复模式,并选举产生新的Leader服务器。新Leader产生并且集群中过半的机器完成状态同步后,ZAB协议退出恢复模式。其中,状态同步就是数据同步。

    在消息广播状态,如果一台新的服务器加入集群,或者失联的机器恢复连接后,该机器会自动进入恢复模式,去寻找Leader服务器并进行状态同步,然后退出恢复模式进入消息广播的状态。

    崩溃恢复的核心的高效可靠的Leader选举算法:一方面,要让Leader服务器自身快速认识到自身已经成为新的Leader;另一方面,还要让集群中的其他机器快速感知到新的Leader服务器。

1、基本特性

    1)ZAB协议确保在Leader服务器上提交的事务,最终要被全部服务器都提交;

    2)ZAB协议确保丢弃只在Leader服务器上被提交的事务。

    这两条基本特性的保证,ZAB协议是通过ZXID(事务编号)来进行保证的。ZAB的Leader选举算法保证,新选举出来的Leader服务器拥有最高的ZXID,这样,这个新的Leader服务器一定拥有所有已提交的事务。同时,这也能省去新Leader检查事务提交和丢弃的过程,只需要要求其他服务器同步自己的状态即可。

2、数据同步

    Leader选举完成后,首先会确认数据同步是否完成。数据同步完成的标志,是事务日志中所有的事务,都被一半以上的Follower提交了。

    ZXID是一个64位的数字。低32位是针对客户端的每一个事务请求的编号,Leader服务器每产生新的事务Proposal,都会对该计数器加1;高32位则代表了Leader周期epoch的编号,每当选举产生新的Leader,就会将Leader服务器本地日志中最大事务的ZXID的epoch加1,并将低32位置0来生成新的ZXID。

    基于上述策略,当一个新机器加入集群,或者崩溃集群恢复连接,如果集群中包含以前周期中的未提交事务,这些事务就会被丢弃;同时,周期落后的机器的ZXID肯定比周期更先进的机器要小,也就不会在选举中成为新的leader。