Zookeeper的Zab协议与拓展(转载+整理)

前言

在前面两篇的博客中,Zookeeper提供的功能包括:配置共享、命名服务、分布式同步、服务注册等。设想一下如果存放Zookeeper服务的机器宕机了,那这些功能不就没得用了嘛!

所以Zookeeper需要维护一个集群,就算一个机器宕机了,还有其他的可以替上。随之而然解决集群中的数据一致性就成为了Zookeeper集群的首要问题。Zookeeper中使用了Zab协议来保证各个ZK之间的同步。

Zab协议有两种模式,崩溃恢复模式和消息广播模式。其中崩溃恢复模式是在服务启动或者leader宕机的时候,原来选举出leader。消息广播模式用来保持各个副本之间的数据一致性。总结就是一句话,崩溃恢复模式来保证服务的高可用性,消息广播模式保持节点之间的数据一致性。

崩溃恢复模式

Zookeeper集群中有三种服务器角色:Leader、Follower、Observe,接下来从Leader选举过程去分析一下在服务启动和Leader宕机两种情况。

服务启动时期选举

假设有三台ZK服务,ZK1,ZK2,ZK3。每一台Zk都有自己的一个MyId来标识。假设ZK1、ZK2先与ZK3启动。

1.每个ZK发出投票。由于是初始情况,ZK1和ZK2都会将自己作为Leader服务器来进行投票,每次投票会包含所推举的服务器的MyId和ZXID(是一个事务的ID,用来唯一标识服务器状态的变更,最初是0)。ZK1发送由于是初始情况,ZK1和ZK2都会将自己作为Leader服务器来进行投票,每次投票会包含所推举的服务器的myid和ZXID。ZK1发送(1,0)ZK2发送(2,0)

2.每个ZK检查接受到投票有效性。如检查是否是本轮投票、是否来自LOOKING状态的服务器。

3.每个ZK统计票数。针对每一个投票,服务器都需要将别人的投票和自己的投票进行PK,PK规则如下

    · 优先检查ZXID。ZXID比较大的服务器优先作为Leader。

    · 如果ZXID相同,那么就比较myid。myid较大的服务器作为Leader服务器。

对于ZK1而言,它的投票是(1, 0),接收ZK2的投票为(2, 0),首先会比较两者的ZXID,均为0,再比较myid,此时ZK2的myid最大,于是更新自己的投票为(2, 0),然后重新投票,对于ZK2而言,其无须更新自己的投票,只是再次向集群中所有机器发出上一次投票信息即可。

4.统计投票。每次投票后,服务器都会统计投票信息,判断是否已经有过半机器接受到相同的投票信息,对于ZK1、ZK2而言,都统计出集群中已经有两台机器接受了(2, 0)的投票信息,此时便认为已经选出了Leader。

5.改变服务器状态。一旦确定了Leader,每个服务器就会更新自己的状态,如果是Follower,那么就变更为FOLLOWING,如果是Leader,就变更为LEADING。

运行期选举

在Zookeeper运行期间,Leader与非Leader服务器各司其职,即便当有非Leader服务器宕机或新加入,此时也不会影响Leader,但是一旦Leader服务器挂了,那么整个集群将暂停对外服务,进入新一轮Leader选举,其过程和启动时期的Leader选举过程基本一致。

假设正在运行的有ZK1、ZK2、ZK3三台服务器,当前Leader是ZK2,若某一时刻Leader挂了,此时便开始Leader选举。选举过程如下

1.变更状态。Leader挂后,余下的非ObZK服务器都会讲自己的服务器状态变更为LOOKING,然后开始进入Leader选举过程。

2.每个ZK会发出一个投票。在运行期间,每个服务器上的ZXID可能不同,此时假定ZK1的ZXID为123,ZK3的ZXID为122;在第一轮投票中,ZK1和ZK3都会投自己,产生投票(1, 123),(3, 122),然后各自将投票发送给集群中所有机器。

3.接收来自各个服务器的投票。与启动时过程相同。

4.处理投票。与启动时过程相同,此时,ZK1将会成为Leader。

5.统计投票。与启动时过程相同。

6.改变服务器的状态。与启动时过程相同。

消息广播模式

客户端每发送一个更新请求,ZooKeeper都会生成一个全局唯一的递增编号,这个编号反映了所有事务操作的先后顺序,这个唯一编号就是事务ID(ZXID),只有更新请求才算是事务请求。

为保证按照事务的ZXID先后顺序来处理,Leader服务器会分别为每个Follower服务器创建一个队列,并将事务的先后顺序放入队列中,并按照FIFO的策略进行消息发送。

收到需要处理的事务后,Follower服务器会首先以及事务日志的形式写入服务器的磁盘中,写入成功后,会向Leader服务器发送ACK响应。当Leader服务器收到超过一半的Follower服务器的ACK响应后,会向所有Follower服务器广播Commit消息,

收到Commit消息的Follower服务器也会完成对事务的提交。

如果接收到事务请求的是Follower服务器,它会将请求转发给Leader服务器处理。

Zookeeper的扩展

ZooKeeper的一致性

zookeeper建议部署的节点个数为奇数个,只有超过一半的机器不可用整个zk集群才不可用。zookeeper集群中主要有两个角色leader和flower,每个客户端可以连接集群中的任何一个zookeeper节点,同时从其上面read信息,但是针对write操作,flower节点会转发给leader,由leader负责原子广播,从而保证集群中各个节点的数据一致性,zookeeper中规定只有当多余一半的节点同步完成整个write操作才算完成。也就是说可能会有少于一半的数据不是新数据,因此zookeeper中不是强一致性而是实现的最终一致性。

Observe角色

Observers 的需求源于 ZooKeeper follower服务器在上述工作流程中实际扮演了两个角色。它们从客户端接受连接与操作请求,之后对操作结果进行投票。这两个职能在 ZooKeeper集群扩展的时候彼此制约。如果我们希望增加 ZooKeeper 集群服务的客户数量(我们经常考虑到有上万个客户端的情况),那么我们必须增加服务器的数量,来支持这么多的客户端。然而,从一致性协议的描述可以看到,增加服务器的数量增加了对协议的投票部分的压力。领导节点必须等待集群中过半数的服务器响应投票。于是,节点的增加使得部分计算机运行较慢,从而拖慢整个投票过程的可能性也随之提高,投票操作的会随之下降。这正是我们在实际操作中看到的问题——随着 ZooKeeper 集群变大,投票操作的吞吐量会下降。

所以需要增加客户节点数量的期望和我们希望保持较好吞吐性能的期望间进行权衡。要打破这一耦合关系,引入了不参与投票的服务器,称为 Observers。 Observers 可以接受客户端的连接,将写请求转发给领导节点。但是,领导节点不会要求 Observers 参加投票。相反,Observers 不参与投票过程,仅仅和其他服务节点一起得到投票结果。