文章目录

  • ZooKeeper
  • 一致性协议:ZAB


ZooKeeper

ZooKeeper是一个开源的分布式协调服务,它可以用来协调和同步多服务器之间的状态。

ZooKeeper 可以作为微服务架构中注册中心的选型,它最需要被关心的也是数据模型和一致性协议。数据模型关乎服务信息在 ZooKeeper 服务中的存储结构,而一致性协议是注册中心服务状态一致性的保障。

首先介绍 ZooKeeper 的数据存储模型,它的数据存储模型就是一棵树的结构,树的一个节点就是 ZooKeeper 的一个数据节点 Znode,而数据的路径及它的命名规则与文件系统类似,由斜杠“/”分割每个层级。下图为 ZooKeeper 的数据存储模型示意图。

SOA 注册中心技术实现zookeeper zookeeper实现注册中心原理_zookeeper

每个 Znode 节点包含三部分内容,分别是子节点的索引、与该节点关联的数据,以及描述该节点的节点属性信息。 其中描述该节点属性信息包括数据版本号、子节点的版本号、ACL的版本号、Znode创建的事务ID、节点创建时的时间戳和节点最新一次更新发生时的时间数等内容。而与该节点关联的数据,在服务注册的场景中,它就代表了服务的描述信息,注册的服务就存储在这里。每个 Znode 节点都有它自己的生命周期,根据生命周期的不同,以及是否排列同级的Znode节点的顺序,可以分为持久节点、持久顺序节点、临时节点和临时顺序节点:

  • 特久节点:Znode节点由客户端发起创建请求,在ZooKeeper服务端完成创建,一旦 Znode 节点被创建了,除非主动对 Znode 执行移除操作,否则这个 Znode 将一直保在ZooKeeper服务中,这种节点不会因为创建它的客户端与ZooKeeper服务端断连或者会话失效而被移除。
  • 持久顺序节点:在持久顺序节点的基础上每个父节点会为它的子节点(不包括孙子节点)维护每个子节点创建的先后顺序信息。可以在创建的节点名称后自动添加该节点在它所在层级的顺序序号,比如“node-2”。
  • 临时节点:Znode节点的生命周期和客户端会话绑定在一起,一旦创建该节点的客户端与服务端之间的会话失效,那么这个客户端创建的所有临时节点都会被ZooKeeper服务端移除
  • 临时顺序节点:在临时节点的基础上每个父节点会为它的子节点(不包括孙子节点)维护每个子节点创建的先后顺序信息

一致性协议:ZAB

在ZooKeeper集群中,如何保证集群内的各个ZooKeeper节点数据一致,是ZooKeeper的重点。ZooKeeper中的一致性协议叫做ZooKeeper Atomic Broadcast(原子消息广播协议),简称ZAB。ZAB规定了改变ZooKeeper服务器上的数据状态的事务请求的流程。

下面从ZAB中的角色、流程、两种模式,以及多服务器事务同步等方面展开介绍:

在ZAB中,ZooKeeper的服务节点可以被分为两类,分别是Leader节点和Follower节点,他们分别代表了ZAB的Leader和Follower两种角色。

  • Leader节点全局唯一,负责协调所要处理的事务,可以用作读或者写操作Leader节点选举会发生在以下三个场景下:
  • 集群初始化启动时。
  • Leader节点崩溃后。
  • 因网络等问题导致Leader节点与集群中超过一半的节点断连后。
  • Follower节点Leader节点的副本,一般用作读操作。 Follower节点中有一种特殊的节点叫做观察者节点,观察者节点不参与投票和选举,观察者节点增加了ZooKeeper的动态扩展能力。因为观察者节点不会影响ZooKeeper集群,它只会听取和执行投票结果,如果收到客户端的写请求,那么它会将写请求转发给Leader节点。除了观察者节点这种特殊的Follower节点,其余正常的Follower节点都会参与投票和选举。

Leader节点和Follower节点在面对客户端的请求时表现是不一样的。下面就是一个客户端发起写请求和读请求,ZooKeeper集群应对不同类型的请求的过程:

(1)ZooKeeper 客户端会随机连接ZooKeeper 集群中的一个节点,当客户端向连接的ZooKeeper 服务节点发起一个写请求,并且当前ZooKeeper服务节点是一个 Follower节点而不是Leader节点时,该节点就会把写请求转发给Leader节点,提交事务,Leader节点接收写请求后会广播该事务;如果是读请求,则直接从该Follower节点中读取数据。

(2)客户端事务请求到达Leader服务器后,Leader节点将该事务转化为一个提议,并将提议分发给集群中所有Follower节点,然后等待Follower节点的反馈。

(3)Follower服务节点如果正常,则会给予Leader节点正确的反馈。

(4)如果Leader服务器收到半数以上的Follower服务器的正确反馈,则Leader向集群中所有Follower服务器分发Commit消息,要求Follower服务器提交该事务。

以上是在各节点都正常的情况下处理请求的流程,从流程中可以看到,所有的写操作都由Leader节点控制,这种正常的执行流程也被称为消息广播模式

消息广播模式中提交事务使用的是原子广播协议类似于2PC (二阶段提交协议)。2PC事务处理过程中有中断逻辑,只要有参与者没有正确反馈,就会中断这次事务提交。但是在ZAB协议中,如果没有半数以上Follower服务器进行ACK反馈,那么就丢弃该Leader服务器,直接进入Leader服务器进行选举。如果收到半数以上的 Follower服务器反馈ACK,则开始提交事务,不需要像2PC一样等待所有Follower服务器反馈。这种模式的触发条件是Leader服务节点被选举,数据同步完成后,此时会切换到消息广播模式。

整个 ZooKeeper 集群除了有消息广播模式,还有另一种模式叫作崩溃恢复模式,整个ZooKeeper集群会在这两种模式中不断切换。

崩溃恢复模式用于解决Leader服务器单点问题,并且在集群恢复后解决数据不一致问题。它的触发条件是当Leader服务器出现异常后,ZooKeeper进入崩溃恢复模式,选举新的Leader服务器。 当Leader服务器出现异常后,ZAB需要一种 Leader选举算法来确保正确并且快速地选出Leader服务器,同时还要让其他机器能够快速感知选举产生的新Leader服务器。一个Leader服务节点需要获得过半节点的支持才能被选举为Leader 节点。

在ZAB协议中规定如果一个事务在一台机器上处理成功,那么就算有机器出现崩溃,这个事务也会被认为在所有机器上都处理成功了,这种现象会导致两种发据不一致的情况。

  • Leader节点收到一个事务,广播给所有的Follower节点,并且已经获得超过半数的ACK反馈,那么Leader节点会提交该事务,并且进入第二阶段,也就是将Commit消息发送给所有的Follower节点,但是在发送Commit消息之前Leader节点崩遗了。也就是只在Leader服务器上提交了事务,但在Follower服务器上并没有提交,这种情况下会导致数据不一致。
  • 为了解决上述情况,当进行Leader节点崩溃恢复操作时,ZAB需要确保弃那些只在Leader服务器上被提交的事务,也就是上述描述的事务。在Leader 节点崩溃恢复过程中,Leader节点已经重新选举,但是上述崩溃Leader节点重新恢复后加入ZooKeeper集群,作为一个Follower节点,携带了先前该节点提交的事务,并且该事务只有它有,而别的节点都没有。出现该情况时需要丢弃该事务,如果不丢弃该事务,则会导致该Follower节点与其他节点的数据不一致。

根据上述两种不一致情况,ZAB的Leader选举算法必须确保最新的事务已经被 Leader提交,并且跳过已经被丢弃的事务。为了保证这两点,选举的Leader节点必须具有最新的事务,也就是拥有机器最高编号(ZXID)的事务。因为这样可以保证新Leader具有所有已经提交的提案,也就不需要进行丢弃事务的工作。所以在选举Leader节点时,除了需要获得半数以上节点的同意,还需要拥有ZXID编号最高的事务。

从上述的两种模式可以看出,ZooKeeper也并不能保证线性一致性,它只能保证顺序一致性。 而在CAP模型中,ZooKeeper选择了分区容错性和一致性,牺牲了一定程度的可用性。比如在进行Leader选举时,整个ZooKeeper集群是不可用的。它并不像Eureka一样,每个节点都有写操作的能力。所以ZooKeeper在一定程度上牺牲了一部分的可用性。