写在前面
在之前的文章中我们针对Zookeeper的会话管理、Watcher机制做了详细的介绍。但是在集群模式下,Zookeeper是如何选主,进行数据同步的呢?本文我们来介绍一下Zookeeper的另一大核心内容——ZAB协议ZAB协议的全称是 Zookeeper Atomic Broadcast(Zookeeper原子广播),是为分布式协调服务Zookeeper专门设计的一种支持崩溃恢复的原子广播协议 ,是Zookeeper保证数据一致性的核心算法。ZAB借鉴了Paxos算法,但又不像Paxos那样,是一种通用的分布式一致性算法。
在Zookeeper中,基于该协议,Zookeeper实现了一种主备模型(即Leader和Follower模型)的系统架构来保证集群中各个副本之间数据的一致性。这里的主备系统架构模型,就是指只有一台客户端(Leader)负责处理外部的写事务请求,然后Leader客户端将数据同步到其他Follower节点。保证一个全局的变更序列被顺序提交。Zookeeper是一个树形结构,很多操作都要先检查才能确定是否可以执行,比如P1的事务t1可能是创建节点"/a",t2可能是创建节点"/a/b",只有先创建了父节点"/a",才能创建子节点"/a/b"。为了保证这一点,ZAB要保证同一个Leader发起的事务要按顺序被提交,同时还要保证只有先前Leader的事务被apply之后,新选举出来的Leader才能再次发起事务。
当主进程出现异常的时候,整个zk集群依旧能正常工作。
ZAB协议包括两种基本的模式:崩溃恢复和消息广播
当整个集群启动过程中,或者当 Leader 服务器出现网络中断、崩溃退出或重启等异常时,ZAB协议就会进入崩溃恢复模式,选举产生新的Leader。当选举产生了新的 Leader,同时集群中有过半的机器与该 Leader 服务器完成了状态同步(即数据同步)之后,ZAB协议就会退出崩溃恢复模式,进入消息广播模式。这时,如果有一台遵守ZAB协议的服务器加入集群,因为此时集群中已经存在一个Leader服务器在广播消息,那么该新加入的服务器自动进入恢复模式:找到Leader服务器,并且完成数据同步。同步完成后,作为新的Follower一起参与到消息广播流程中。上面提到根据ZAB协议内容,我们将其分为两种模式,即崩溃恢复和消息广播。如果进一步拆分,我们可以拆分为三个阶段,在崩溃恢复拆分为选举阶段和数据恢复阶段,消息广播对应消息广播阶段
服务器启动时
我们以三台机器组成的服务器集群为例。在服务器集群初始化阶段,当有一台服务器(我们假设这台机器的myid为1,一次称其为Server1)启动时,它是无法完成Leader选举的。当第二台机器(我们假设这台机器的myid为2,一次称其为Server2)也启动后,此时这两台机器已经能够进行互相通信,每台机器都试图找到一个Leader,于是便进入Leader选举流程由于是初始情况,因此对于Server1和Server2来说,都会将自己作为Leader服务器来进行投票,每次投票包含的最基本的元素包括:所推举的服务器的myid和zxid,我们以(myid,zxid)的形式来表示。因为是初始化阶段,因此无论是Server1还是Server2,都会投票给自己,即Server的投票为(1,0),Server2的投票为(2,0),然后各自将这个投票发给集群中其他所有机器
每个服务器向自己投票的同时,都会接受来自其他服务器的投票。集群中的每个服务器在接收到投票后,首先会判断该投票的有效性,包括检查是否是本轮投票,是否来自Looking状态的服务器。无论是哪种情况,此时集群中的所有机器都处于一种试图选举出一个 Leader的状态,我们把这种状态称为"LOOKING",意思是说正在寻找 Leader。当一台服务器处于LOOKING状态的时候,那么它就会向集群中所有其他机器发送消息,我们称这个消息为"投票"。
在接收到来自其他服务器的投票后,针对每一个投票,服务器都需要将别人的投票和自己的投票进行 PK,PK 的规则如下。现在我们来看Server1和Server2实际是如何进行投票处理的。对于Server1来说,它自己的投票是(1,0),而接收到的投票为(2,0)。首先会对比两者的 zxid,因为都是0,所以无法决定谁是Leader。接下来会对比两者的myid,Server1发现接收到的投票中的myid是2,大于自己,于是就会更新自己的投票为(2,0),然后重新将投票发出去。而对于Server2来说,不需要更新自己的投票信息,只是再一次向集群中所有机器发出上一次投票信息即可。
每次投票后,服务器都会统计所有投票,判断是否已经有过半的机器接收到相同的投票信息。对于Server1和Server2服务器来说,都统计出集群中已经有两台机器接受了(2,0)这个投票信息。对于这里由3台机器构成的集群,大于等于2台即为达到"过半"要求,即大于或等于(n/2+1)。当 Server1和Server2都收到相同的投票信息(2,0)的时候,即认为已经选出了 Leader。
一旦确定了Leader,每个服务器就会更新自己的状态:如果是 Follower,那么就变更为FOLLOWING,如果是Leader,那么就变更为LEADING
服务器运行时
在ZooKeeper集群正常运行过程中,一旦选出一个Leader,那么所有服务器的集群角色一般不会再发生变化。也就是说,Leader服务器将一直作为集群的Leader,即使集群中有非Leader节点挂了或是有新机器加入集群也不会影响 Leader。但是一旦Leader 所在的机器挂了,那么整个集群将暂时无法对外服务,而是进入新一轮的Leader选举。服务器运行期间的Leader 选举和启动时期的Leader 选举基本过程是一致的。我们假设当前正在运行的ZooKeeper服务器由3台机器组成,分别是 Server1、Server2和Server3,当前的Leader是Server2。假设在某一个瞬间,Leader挂了,这个时候便开始了Leader 选举。当 Leader 挂了之后,余下的非Observer服务器(不参与选举的服务节点)都会将自己的服务器状态变更为LOOKING,然后开始进入Leader选举流程。 在这个过程中,需要生成投票信息(myid,ZXID)。因为是运行期间,因此每个服务器上的ZXID可能不同,我们假定Server1的ZXID为123,而Server3的ZXID为 122。在第一轮投票中,Server1和Server3都会投自己,即分别产生投票(1,123)和(3,122),然后各自将这个投票发给集群中所有机器。
对于投票的处理,和上面提到的服务器启动期间的处理规则是一致的。在这个例子里面,由于Serverl的ZXID为123,Server3的ZXID为122,那么显然,Serverl会成为Leader。关于Zxid
在ZAB的事务编号zxid设计中,zxid是一个64位的数字。其中低32位可以看成一个简单的单增计数器,针对客户端每一个事务请求,Leader在产生新的Proposal事务时,都会对该计数器加1。而高32位则代表了Leader周期的 epoch 编号。epoch编号可以理解为当前集群所处的年代或者周期。每次Leader变更之后都会在 epoch 的基础上加1,这样旧的Leader崩溃恢复之后,其他Follower也不会听它的了,因为Follower只服从epoch最高的 Leader 命令。每当选举产生一个新的Leader,就会从这个Leader服务器上取出本地事务日志中最大编号Proposal的zxid,并从zxid 中解析得到对应的epoch 编号,然后再对其加1,之后该编号就作为新的epoch值,并将低32位数字归零,由0开始重新生成zxid。
成为Leader的条件
服务器数量最好为奇数台
集群的脑裂通常是发生在节点之间通信不可达的情况下,集群会分裂成不同的小集群。小集群各自选出自己的master节点,导致原有的集群出现多个master节点的情况,这就是脑裂。
假如zookeeper集群有 5 个节点,发生了脑裂,脑裂成了A、B两个小集群:
A:1个节点 ,B:4个节点
A:2个节点, B:3个节点
可以看出,上面这两种情况下,A、B中总会有一个小集群满足可用节点数量 > 总节点数量/2。所以zookeeper集群仍然能够选举出leader,仍然能对外提供服务,只不过是有一部分节点失效了而已。假如zookeeper集群有4个节点,同样发生脑裂,脑裂成了A、B两个小集群:
A:1个节点 , B:3个节点
A:2个节点 , B:2个节点
可以看出,第一种情况满足选举条件的,与第一个示例相同。但是第二种情况就不同了,因为A和B都是2个节点,都不满足可用节点数量 > 总节点数量/2的选举条件, 所以此时zookeeper就彻底不能提供服务了。
综合上面两个例子可以看出: 在节点数量是奇数个的情况下, zookeeper集群总能对外提供服务(即使损失了一部分节点)。如果节点数量是偶数个,会存在zookeeper集群不能用的可能性(脑裂成两个均等的子集群的时候)
在集群中,服务器的个数并不是必须为奇数,只是使⽤偶数会使得系统更加脆弱。假设在集群中使⽤4个服务器,那么多数原则(集群数量内活跃的节点数 > 非活跃的节点数)对应的数量为3个服务器。然⽽,这个系统仅能容许1个服务器崩溃,因为两个服务器崩溃就会导致系统失去多数原则的状态。因此,在4个服务器的情况下, 我们仅能容许⼀个服务器崩溃。这和集群中有三台服务器的容错能力是相同的,但却浪费了一台服务器的资源。所以奇数台是更节省服务器资源的方案
以上就是ZAB协议中选举流程的内容,下一篇文章我们来介绍一下崩溃恢复的另一个阶段,即数据恢复与同步阶段的详细流程,敬请期待吧~~