一文看懂 ZooKeeper ,面试再也不用背八股

本文将以如下内容为主线讲解ZooKeeper中的学习重点,包括 ZooKeeper 中的角色、ZAB协议、数据模型、选举机制、监听器原理以及应用场景等。会对相关的面试题或开发中常见内容,进行重点讲解。
在这里插入图片描述
接下来将带领大家入门学习 ZooKeeper 系列的内容,力求通俗易懂,图文并茂。

一、ZooKeeper 的工作机制

1. 什么是ZooKeeper

ZooKeeper 是一个分布式协调服务,其设计的初衷是为分布式软件提供一致性服务。其本质上,就是文件系统+通知机制。

ZooKeeper 提供了一个类似 Linux 文件系统的树形结构,ZooKeeper 的每个节点既可以是目录也可以是数据,并且 ZooKeeper 还提供了对每个节点的监控与通知机制。

2. ZooKeeper 的工作机制

ZooKeeper 采用的是主从模式,有主节点和从节点。从设计模式的角度,它是一个基于观察者模式设计的分布式服务管理框架,负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发生变化,ZooKeeper 就将负责通知已经注册的那些观察者做出相应的反应。

3. ZooKeeper 的角色及其关系

ZooKeeper 中的角色包括 Leader、Follower、Observer。Leader 是集群主节点,主要负责管理集群状态和接收用户的写请求;Follower 是从节点,主要负责集群选举投票和接收用户的读请求;Observer 的功能与 Follower 类似,只是没有投票权,主要用于分担 Follower 的读请求,降低集群的负载。
在这里插入图片描述

a. Leader

一个运行中的 Zookeeper 集群只有一个 Leader 服务,Leader 服务主要包括以下两个指责:

  • 负责集群数据的写操作。所有写操作必须要 Leader 完成之后,才可以将写操作广播到其他 Follower,并且只有超过半数节点(不包括 Observer)写入成功后,这些写请求才算写成功;
  • 发起并维护各个 Follower 以及 Observer 之间的心跳,以监控集群的运行状态。

b. Follower
一个 Zookeeper 集群可以有多个 Follower,Follower通过心跳与 Leader 保持连接。Follower 服务主要有以下两个指责:

  • 负责集群数据的读操作。Follower 在接受到一个客户端请求之后,会先判断该请求是读请求还是写请求,若为读请求,则 Follower 从本地节点上读取数据并返回给客户端;若为写请求,则 Follower 会将写请求转发给 Leader 来处理。
  • 参与集群中 Leader 的选举。当 Leader 失效之后,Follower 需要在集群选举时进行投票;(后续会详细讲解选举机制)

c. Observer

一个 Zookeeper 集群可以有多个 Observer,Observer 的主要职责是负责集群数据的读操作,其功能同以上介绍的 Follower 的功能类似,主要的差别就是 Observer 没有投票权。

4. 面试题:Follower 已经具备了 Observer 的所有功能,为什么还要设计 Observer 角色 ?

这是因为 ZooKeeper 集群在运行过程中要支持更多的客户端并发的操作,就需要增加更多的服务实例,而过多的服务实例会使得集群的投票阶段变得复杂,选举时间过长不利于集群故障的快速恢复。
因此,ZooKeeper 引入了 Observer 角色,Observer 不参与投票,只负责接收客户端来的读请求,以及将写请求转发给 Leader 。加入更多的 Observer 节点,不仅提高了 ZooKeeper 集群的吞吐量,保障了系统的稳定性。


二、ZooKeeper 中的 ZAB 协议

前面介绍了 ZooKeeper 的基本概念、角色、工作机制等内容,为了后续更好地学习 ZooKeeper 的原理,本节先给大家介绍一种分布式一致性协议——ZAB(ZooKeeper Atomic Broadcast,ZooKeeper 原子消息广播协议)。

1. 什么是ZAB

在介绍 ZAB 之前,先给大家介绍下 ZooKeeper 的由来。

ZooKeeper 最早起源于雅虎研究院的一个研究小组,当时雅虎内部很多大型系统都需要依赖一个类似的系统来进行分布式协调,为了让把精力集中在业务逻辑上,雅虎研发人员开发了一个通用的解决了单点问题的分布式协调框架。

ZooKeeper 名字的由来:
立项之初,考虑到很多项目都是使用动物的名字来命名的,雅虎的工程师希望给这个项目也取一个动物的名字。有人开玩笑地说:“在这样下去,我们这儿就变成动物园了!”此话一出,大家纷纷表示就叫动物园管理员吧。

而 ZAB 协议最初也只是为雅虎内部那些高吞吐量、低延迟的分布式系统场景设计的,它并不是一种通用的分布式一致性算法,而是专门为 ZooKeeper 设计的一种支持崩溃恢复的原子广播协议,实现了一种主备模式的系统架构来保持集群中各副本之间数据的一致性。

2. 两个概念

1)当前集群的周期号:Epoch

集群中每次 Leader 的重新选举都会产生一个新的周期号(也有叫年代号的,都一个意思),周期号的产生规则则是在上一个周期号上加1,这样当之前的 Leader 奔溃恢复后会发现自己的周期号比当前的周期号小,说明此时集群已经产生了一个新的 Leader,旧的 Leader 会再次以 Follower 的角色加入集群。

2)ZAB 协议的事务编号:Zxid

Zxid 是一个64位的数字,其中低32位存储的是一个简单的单调递增的计数器,针对客户端的每个事务请求,计数器都会加1。高32位存储的是 Leader 的周期号 Epoch。每次选举产生一个新的 Leader 时,该 Leader 就会从当前服务器的日志中取最大事务的 Zxid,获取其中高32位的 Epoch 值并加1,以此作为一个新的 Epoch,并会将低32位从0开始重新计数。

3. 两种模式

1)集群选主:恢复模式

当集群 Leader 出现崩溃,或者由于网络原因导致 Leader 和过半的 Follower 失去联系,那么集群将开始选主,该过程为恢复模式。

Leader 的选举机制不仅可以让 Leader 节点知道自身被选举为 Leader,同时还能需要让集群中其他节点也能快速感知到选举的新的 Leader 节点。

2)数据同步:广播模式

当 Leader 被选举出来后,Leader 将最新的集群状态广播给其他 Follower,该过程为广播模式。在半数以上的 Follower 完成与 Leader 的状态同步后,广播模式结束。

4. ZAB 的实现过程

1)选举阶段

选举阶段的目的就是产生一个准 Leader,节点在一开始都处于选举阶段,只要有一个节点得到超过半数节点的票数,那么它就可以当选准Leader,只有到达第三阶段(同步阶段)这个准Leader才会成为真正的Leader。
在这里插入图片描述

2)发现阶段

在该阶段中 Follower 和上一阶段选举出的准 Leader 进行通信,同步 Follower 最近接收的事务提议。这一阶段的目的是发现当前大多数节点接收的最新提议,并且准 Leader 生成新 的epoch,然后让 Follower 接收,更新它们的acceptedEpoch。
在这里插入图片描述

如果节点1认为节点L是 Leader,那么当节点1尝试连接节点L时,如果连接遭到拒绝,则集群将会重新进入选举阶段。

3)同步阶段

同步阶段主要是将 Leader 在前一阶段获得的最新提议信息同步到集群中所有的副本。并且只有当超过半数的节点都同步完成后,准 Leader 才会成为真正的 Leader 。Follower 只会接收 Zxid比自己 lastZxid 大的提议。
在这里插入图片描述
同步完成之后,集群的选主操作才算完成,新的 Leader 将产生。

4)广播阶段

在该阶段,ZooKeeper 集群正式对外提供事务服务,这时 Leader 进行消息广播,将其上的状态通知到其他 Follower。若后续有新的节点加入进来,则 Leader 会对新节点进行状态同步。


三、数据模型与监听器

ZooKeeper 可用于统一命名服务、配置管理、集群管理、分布式通知协调、分布式锁等场景,在这些应用场景中, ZooKeeper 内部是如何做到分布式数据一致性的呢?本节将给大家介绍下 ZooKeeper 内部是如何做到分布式数据一致性的。

ZooKeeper 使用了一个树形结构的命名空间来表示其数据结构,其视图结构和标准的 Unix 文件系统非常类似,但没有引入传统文件系统中目录和文件等相关概念,而是将 ZooKeeper 树中的每一个节点都称之为一个 Znode。其数据结构如下图所示。

在这里插入图片描述
类似文件系统的目录树,ZooKeeper 树中的每个节点都可以拥有子节点,而不同的是,每个 Znode 节点都存储了数据信息,同时也提供了对节点信息的监控等操作。

1. Znode 的数据模型

Znode 是 ZooKeeper 中数据的最小单元,每个 Znode 都兼具文件和目录两种特点,既能像文件一样保存和维护数据,又可以由一系列使用斜杠(/)进行分割的方式作为路径标识的一部分。每个 Znode 都有以下三部分组成。

  • Stat:状态信息,用于存储该 Znode 的版本、权限、时间戳等信息;
  • Data:实际存储的数据;
  • Children:对子节点的信息描述;

需要特别说明的是,Znode 节点虽然可以存储数据信息,但它并不能像数据库那样存储大量的数据,Znode 的设计初衷就是存储分布式应用中的配置文件、集群状态等元数据信息。

2. Znode 的控制访问

1)ACL

ACL(Access Control List) 为访问控制列表,应用程序会根据实际需求将用户分为只读、只写以及读写用户,每一个 Znode 节点都会有一个 ACL 用来约束不同的用户对节点的访问权限。

2)原子操作

每一个 Znode 节点上都具有原子操作的特性,读操作将获取与节点相关的数据,写操作将替换节点上的数据,上期文章中讲到 ZooKeeper 都会为每一个事务请求分配一个全局唯一的事务编号 Zxid,每一个 Zxid 就对应一次更新操作,并可以通过 Zxid 来识别出更新操作请求的全局顺序。

3. Znode 的节点类型

ZooKeeper 中的节点有两种,分别为临时节点和永久节点。节点的类型在创建时即被确定,并且不能改变。

1)临时节点

临时节点常被应用于心跳监控,举个栗子,设置过期时间为 30s,要求各个子节点对应的服务端每 5s 发送一次心跳到 ZooKeeper 集群,当服务端连续 30s 没有向 ZooKeeper 汇报心跳信息,也就是连续6次没有收到心跳信息,就认为该节点宕机了,并将其从服务列表中移除。

临时节点的生命周期依赖于创建它们的会话(Session)。一旦会话结束,临时节点将被自动删除,当然可以也可以手动删除。

另外,ZooKeeper 的临时节点不允许拥有子节点。

2)永久节点

永久节点的数据会一直存储着,直到用户调用接口将其数据删除,该节点一般用于存储一些永久性的配置信息。

4. Znode 的监听器机制( 面试重点

ZooKeeper 的每个节点上都有一个 Watcher 用于监控节点数据的变化,当节点状态发生改变时(Znode 新增、删除、修改)将会触发 Wahcher 所对应的操作。在 Watcher 被触发时,ZooKeeper 会向监控该节点的客户端发送一条通知说明节点的变化情况。
在这里插入图片描述
具体实现流程就是,客户端向 ZooKeeper 服务器注册 Watcher 的同时,会将 Watcher 对象存储在客户端的 WatchManager 中。当 ZooKeeper 服务器端触发 Watcher 事件后,会向客户端发送通知,客户端线程从 WatchManager 中取出对应的 Watcher 对象来执行回调逻辑。


四、ZooKeeper 的选举机制和流程

我们在本系列的第一期就介绍了 ZooKeeper 集群中的三个服务器角色:Leader、Follower 和 Observer。其中,Leader 选举是 ZooKeeper 中最重要的技术之一,也是保证分布式数据一致性的关键所在。本期内容将重点讲解 Leader 是如何被选举的。

1. 选举机制概述

Zookeeper 在配置文件中并没有指定 Master 和 Slave。但是,Zookeeper 工作时, 是有一个节点为 Leader,其他则为 Follower,而这个 Leader 是通过内部的选举机制临时产生的。

每个 Server 首先都提议自己是 Leader,并为自己投票,然后将投票结果与其他 Server 的选票进行对比,权重大的胜出,使用权重较大的选票更新自身的投票箱,我们介绍下服务器启动时期的 Leader 选举。

1)每一个 Server 都会发出一个投票

在集群初次启动时,每个 Server 都会推荐自己为 Leader,然后各自将这个投票发给集群中其他 Server。

2)接收来自各个 Server 的投票

每个 Server 在接收到其他 Server 的投票后,首先会判断该票的有效性,包括检查是否本轮投票,是否来自 Looking 状态的 Server。(Looking 状态表示当前集群正处于选举状态)

3)处理投票

针对每一个投票,Server 都会将别人的投票和自己的投票进行 PK,计算出 Zxid 最大的 Server,并将该 Server 设置成下一次投票推荐的 Server。

4)统计投票

每次投票结束之后,都会统计所有投票,获取投票最多的 Server 将成为获胜者,如果获胜者的票数超过集群个数的一半,则该 Server 将为推选为 Leader。否则继续投票,直至 Leader 被选举出来。

5)改变服务器状态

一旦 Leader 确定后,Leader 会通知其他 Follower 集群已经成为 Uptodate 状态,Follower 在收到 Uptodate 消息后,接收 Client 的请求并开始对外提供服务。

2. 选举 Leader 的具体实例

上述的选举过程比较抽象,我们以一个有5个节点的集群为例,目前均为 shutdown 状态。

在这里插入图片描述
按照 Server 的编号依次启动,看下整个的选举过程是如何实现的。

1)Server 1 启动

在这里插入图片描述
Server 1 启动后会提议自己为 Leader 并为自己投票,然后将投票结果发送给其他 Server,由于其他 Server 还未启动,因此收不到任何反馈信息,此时 Server 1 会处于 Looking 状态。

2)Server 2 启动
在这里插入图片描述

Server 2 启动后会提议自己为 Leader 并为自己投票,然后与 Server 1 交换投票结果,由于 Server 2 的编号大于 Server 1,因此 Server 2 胜出。但是,由于投票数未过集群数的一半,两个 Server 仍然均处于 Looking 状态。

3)Server 3 启动
在这里插入图片描述
Server 3 启动后会提议自己为 Leader 并为自己投票,然后与 Server 2、Server 3 交换投票结果,由于 Server 3 的编号最大,因此 Server 3 胜出。此时, Server 3 的票数大于集群数的一半了,因此 Server 3 会更新为 Leader ,Server 1、Server 2 更新为 Follower。

4)Server 4 启动
在这里插入图片描述
Server 4 启动后会提议自己为 Leader 并为自己投票,然后与 Server 1、Server 2、Server 3 交换投票结果,发现 Server 3 已经成为了 Leader,因此 Server 4 也成为了 Follower。

5)Server 5 启动
在这里插入图片描述

与 Server 4 一样,Server 5 启动后会给自己投票,然后与其他 Server 交换信息,发现 Server 3 已经成为了 Leader,因此 Server 5 也成为了 Follower。

投票 vote 的数据结构

  • id:服务器ID,用来唯一标识 ZooKeeper 集群中的服务器;
  • Zxid:事务ID,用来唯一标识一次服务器状态的变更;
  • electionEpoch:代表当前服务器的选举轮次,是一个自增序列;
  • peerEpoch:被推举的 Leader 的选举轮次;
  • state:当前服务器的状态。

 

posted @ 2022-05-20 18:47  Data跳动  阅读(1108)  评论(0编辑  收藏  举报