对于使用过Zookeeper的开发人员来说,都应该对ZAB协议有所耳闻。它是为分布式协调服务Zookeeper专门设计的一种支持崩溃恢复的原子广播协议,全称是Zookeeper Atomic Broadcast(Zookeeper原子消息广播协议)。今天就介绍一下ZAB协议主要包括哪些内容,它是如何来实现来保证数据一致性的。
ZAB协议主要包括两种基本的模式,消息广播和崩溃恢复。当整个服务框架在启动的过程中或是当Leader服务器出现中断,退出,重启等异常情况,ZAB协议就会进入恢复模式并选举产生新的Leader服务器。当选举产生了新的Leader服务器,同时集群中已经有过半的机器与该Leader服务器完成了数据同步之后,ZAB协议就会退出恢复模式,进入消息广播模式了。
消息广播
类似于一个二阶段提交协议,不同的是,它移除了中断的逻辑处理。也就是说Follower服务器要么正常反馈Leader提出的事务,要么就抛弃Leader服务器。首先针对客户端的事务请求,Leader服务器会为其生成对应的事务Proposal,并将其广播给所有的Follower服务器,收集各自的选票,最后进行事务的提交。
这里ZAB协议规定了,只要Leader服务器收到了过半的Ack响应,就可以提交事务,并向所有的Follower服务器发送commit消息,通知Follower服务器进行事务提交。在消息广播的过程中,Leader服务器会给Proposal分配一个全局单调递增的唯一ID,我们称之为事务ID(ZXID),由于ZAB协议需要保证每一个消息的严格的因果关系,因此必须将每一个事务Proposal按照其ZXID的先后顺序来进行排序与处理。在消息广播过程中,Leader服务器会为每一个Follower服务器分配一个单独的队列,然后将需要的广播的事务Proposal依次放入到这些队列中去,并且根据FIFO策略进行消息发送。每一个Follower服务器在接收到这个事务Proposal之后,都会首先将其以事务日志的形式写入到本地磁盘中,并且在成功写入后反馈给Leader服务器一个Ack响应。当Leader服务器收到过半的Ack响应后,就会先提交事务,接着发送commit消息给所有的Follower服务器,Follower服务器收到消息后,也完成事务的提交。
崩溃恢复
当我们的Leader服务器在收到Ack消息后,完成事务的提交,还没来的及向Follower服务器发送commit消息,就挂掉了,那么整个集群便会进入恢复模式,进行Leader选举。ZAB协议规定了如果有事务在一台服务器上完成了提交,那么它应该在所有的服务器上都完成提交。另外如果Leader服务器在接收事务后还没来的及广播就挂掉了,ZAB协议确保该事务被丢弃。基于这两点,如果让Leader选举算法能够保证新选举出来的Leader服务器拥有集群中所有机器最高编号(即ZXID最大)的事务Proposal,那么就可以保证这个新选举出来的Leader一定具有所有已经提交的事务。
数据同步
完成Leader选举之后,在正式开始工作之前,Leader服务器会首先确认事务日志中的所有Proposal是否都已经被集群中过半的机器提交了,即是否完成数据同步。Leader服务器需要确保所有的Follower服务器能够接受到每一条事务Proposal,并且能够正确地将所有已经提交了的事务Proposal应用到内存数据库中,Leader服务器会为每一个Follower服务器都准备一个队列,并将那些没有被各Follower服务器同步的事务以Proposal消息的形式逐个发送给Follower服务器,并在每一个Proposal消息后面紧接着再发送一个Commit消息,以表示该事务已经被提交。等到该Follower服务器将所有其尚未同步的事务Proposal都从Leader服务器上同步过来并成功应用到本地数据库中后,Leader服务器就会将该Follower服务器加入到真正的可用Follower列表中,并开始之后的其他流程。
关于ZAB协议的主要内容就是以上这几点,下面开始提出我的问题。
1.follower服务器是如何感知当前leader服务器是否正常工作?
Leader服务器和Follower服务器之间是通过心跳检测机制来感知彼此的情况,如果Leader能够在超时时间内正常收到心跳检测,那么Follower就会一直与该Leader保持连接。而如果在指定的超时时间内Leader无法从过半的Follower服务器那里收到心跳检测,或者TCP连接本身断开了,那么Leader就会终止对当前周期的领导,并转换到LOOKING状态,所有的Follower也会选择放弃这个Leader,同时转换到LOOKING状态。之后,所有进程就会开始新一轮的Leader选举。
2.当Leader服务器已经收到客户端请求的事务,并已经广播给所有的Follower服务器,此时,在还未接收到过半的Ack消息,Leader服务器挂掉了,那么该事务最终是会被丢弃还是被提交?
根据Leader选举算法,我们可以看出,各个服务器会选举出拥有ZXID最大的那个事务的服务器作为Leader服务器,只要该事务被提出,并且保证有Follower服务器收到,那么新选举的Leader服务器一定包含了这个事务,这个事务最终会被提交。
3.ZAB协议是如何处理那些需要被丢弃的事务Proposal的?
在ZAB协议的事务编号ZXID设计中,ZXID是一个64位的数字,其中低32位可以看作是一个简单的单调递增的计数器,针对客户端的每一个事务请求,Leader服务器在产生一个新的事务Proposal的时候,都会对该计数器进行加1操作,而高32位则代表了Leader周期epoch的编号,每当选择产生一个新的Leader服务器,就会从这个Leader服务器上取出其本地日志中最大事务Proposal的ZXID,并从该ZXID中解析出对应的epoch值,然后再对其进行加1的操作,之后就会以此编号作为新的epoch,并将低32位置0来开始生成新的ZXID。ZAB协议中的这一通过epoch编号来区分Leader周期变化的策略,能够有效地避免不同的Leader服务器错误地使用相同的ZXID编号提出不一样的事务Proposal的异常情况。当一个包含了上一个Leader周期中尚未提交过的事务Proposal的服务器启动时,其肯定无法成为Leader,因为当前集群中一定包含一个Quorum集合,该集合中的机器一定包含了更高epoch的事务Proposal,因此这台机器的事务Proposal肯定不是最高,也就无法成为Leader了。当这台机器加入到集群中,以Follower角色连接上Leader服务器之后,Leader服务器会根据自己服务器上最后被提交的Proposal来和Follower服务器的Proposal进行比对,比对的结果当然是Leader会要求Follower进行一个回退操作,回退到一个确定已经被集群中过半机器提交的最新的事务Proposal。