ZAB协议的核心是,定义了如何处理那些会改变Zookeeper服务器数据状态的事务请求。即:
所有的事务请求(会改变服务器数据状态的请求,如修改节点数据、删除节点等)必须由一个全局唯一的服务器来协调处理,该服务器被称为Leader,而剩余的其他服务器被称为Follower。Leader负责将一个客户端的事务请求转换成一个事务Proposal(提议),并将该Proposal分发给集群中的所有Follower。然后Leader等待所有Follower的反馈结果,一旦有超过半数的Follower做出了正确的反馈后,Leader就会向所有的Follower再次发送Commit消息,要求将前一个Proposal提交。
在整个消息广播的过程中,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消息后,就会完成对事务的提交。
在正常运行的情况下,ZAB协议会一直处于阶段三来反复地进行消息广播流程。如果出现Leader崩溃或者其他原因导致Leader缺失,ZAB协议就会再次进入阶段一,重新选举新的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选举。