ZooKeeper 学习笔记

概述

ZooKeeper 是一个分布式协调服务,其设计初衷是为分布式软件提供一致性服务。ZooKeeper 提供了一个类似 Linux 文件系统的树形结构,ZooKeeper 的每个节点既可以是目录,也可以是数据,同时 ZooKeeper 提供了对每个节点的监控与通知机制。基于 ZooKeeper 的一致性服务,可以方便地实现分布式锁、分布式选举、服务发现和监控、配置中心等功能

ZooKeeper 是一个基于主备复制的高可用集群,ZooKeeper 的角色包括 Leader、Follower、Observer

Leader:一个运行中的 ZooKeeper 集群只有一个 Leader,Leader 主要有两个职责:一是负责集群数据的写操作,二是发起并维护各个 Follower 及 Observer 之间的心跳以监控集群的运行状态。在 ZooKeeper 集群中,所有写操作都必须经过 Leader,只有在 Leader 写操作完成后,才将写操作广播到其他 Follower。只有超过半数的节点(不包括 Observer 节点)写入成功时,该写请求才算写成功

Follower:一个 ZooKeeper 集群可以有多个 Follower,Follower 通过心跳和 Leader 保持连接。Follower 主要有两个职责:一是负责集群数据的读操作,二是参与集群的 Leader 选举。Follower 在接收到一个客户端请求后会先判断该请求是读请求还是写请求,如果是读请求,则 Follower 从本地节点上读取数据并返回给客户端,如果是写请求,则 Follower 将写请求转发给 Leader 处理。同时,在 Leader 失效后,Follower 需要在集群选举时进行投票

Observer:一个 ZooKeeper 集群可以有多个 Observer,Observer 主要负责对集群数据的读操作。Observer 的功能与 Follower 类似主要区别是 Observer 无投票权。ZooKeeper 集群在运行过程中要支持更多的客户端并发操作,就需要增加更多的服务实例,而过多的服务实例会使集群的投票阶段变得复杂,集群选主时间过长,不利于集群故障的快速恢复。因此,ZooKeeper 引入 Observer 角色,Observer 不参与投票,只用于接收客户端的连接并响应客户端的读请求,将写请求转发给 Leader 节点,加入更多的 Observer 节点不仅提高了 ZooKeeper 集群的吞吐量,也保障了系统的稳定性

ZAB 协议

ZAB(ZooKeeper Atomic Broadcast,ZooKeeper 原子消息广播)协议要通过唯一的事务编号 Zxid(ZooKeeper Transaction id)保集群状态的唯一性

  1. Epoch:指当前集群的周期号(年代号),集群的每次 Leader 变更都会产生一个新的周期号,周期号的产生规则是在上一个周期号的基础上加 1,这样在之前的 Leader 崩溃恢复后会发现自己的周期号比当前的周期号小,说明此时集群已经产生了新的Leader,老的 Leader 会再次以 Follower 的角色加入集群
  2. Zxid:指 ZAB 协议的事务编号,是一个 64 位的数字,其中低 32 位存储的是一个简单的单调递增的计数器,针对客户端的每一个事务请求,计数器都加 1。高 32 位存储为是 Leader 的周期号 Epoch。在每次选举产生一个新的 Leader 时,该 Leader 都会从当前服务器的日志中取出最大事务的 Zxid,获取其中高 32 位的 Epoch 值并加 1,以此作为新的 Epoch,并将低 32 位从 0 开始重新计数

ZAB 协议有两种模式,分别是恢复模式(集群选主)和广播模式(数据同步)

  1. 恢复模式:集群在启动、重启或者 Leader 崩溃后,将开始选主,该过程为恢复模式
  2. 广播模式:Leader 在被选举出来后,会将最新的集群状态广播给其他 Follower,该过程为广播模式。在半数以上的 Follower 完成与 Leader 的状态同步后,广播模式结束

ZAB 协议的四个阶段

  1. 选举阶段(Leader Election):在集群选举开始时,所有节点都处于选举阶段。当某一个节点的票数超过半数节点后,该节点将被推选为准 Leader。选举阶段的目的就是产生一个准 Leader。只有到达广播阶段(Broadcast)后,准 Leader 才会成为真正的 Leader
  2. 发现阶段(Discovery):在发现阶段,各个 Follower 开始和准 Leader 通信,同步 Follower 最近接收的事务提议。这时,准 Leader 会产生一个新的 Epoch,并尝试让其他 Follower 接收该 Epoch 后再更新到本地。发现阶段的一个 Follower 只会连接一个 Leader,如果节点 1 认为节点 2 是 Leader,则当节点 1 尝试连接节点 2 时,如果连接被拒绝,则集群会进入重新选举阶段
  3. 同步阶段(Synchronization):同步阶段主要是将 Leader 在前一阶段获得的最新提议信息同步到集群中所有的副本,只有当半数以上的节点都同步完成时,准 Leader 才会成为真正的 Leader。Follower 只会接收 Zxid 比自己的 lastZxid 大的提议
  4. 广播阶段(Broadcast):在广播阶段,ZooKeeper 集群开始正式对外提供事务服务,这时 Leader 进行消息广播,将其上的状态通知到其他 Follower,如果后续有新节点加入,则 Leader 会对新节点进行状态同步

ZooKeeper 的选举机制和流程

ZooKeeper 的选举机制被定义为:每个 Server 首先都提议自己是 Leader,并为自己投票,然后将投票结果与其他 Server 的选票进行对比,权重大的胜出,使用权重较大的选票更新自身的选票箱

具体选举过程如下:

  1. 每个 Server 在启动后询问其他 Server 给谁投票,其他 Server 根据自己的状态回复自己推荐的 Leader 并返回对应的 Leader id和 Zxid。在集群初次启动时,每个 Serve 都会推荐自己为 Leader
  2. Server 在收到所有其他 Server 的回复后,会计算 Zxid 最大的 Server,并将该 Server 设置成下一次要投票推荐的 Server
  3. 在计算过程中,票数最多的 Server 将成为获胜者,如果获胜者的票数超过集群个数的一半,则该 Server 将被推选为 Leader。否则,继续投票,直到 Leader 被选举出来
  4. Leader 等待其他 Server 连接
  5. Follower 连接 Leader,将最大的 Zxid 发送给 Leader
  6. Leader 根据 Follower 的 Zxid 确定同步点,至此,选举阶段完成

Zookeeper 的数据模型

ZooKeeper 使用一个树形结构的命名空间来表示其数据结构,类似文件系统的目录树。ZooKeeper 树中的每个节点都被称为一个 Znode,ZooKeeper 树中的每个节点都可以拥有子节点,ZooKeeper 的每个节点都存储数据信息,同时提供对节点信息的监控操作

Znode 由三个部分组成:

  1. Stat:状态信息,用于存储该 Znode 的版本、权限、时间戳等信息
  2. Data:Znode 具体存储的数据
  3. Children:Znode 子节点的信息描述

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

Znode 的控制访问:

  1. ACL:每一个 Znode 节点都拥有一个访问控制列表(Access Control List,ACL),该列表规定了用户对节点的访问权限,应用程序可以根据需求将用户分为只读、只写和读写用户
  2. 原子操作:每一个 Znode 节点上的数据都具有原子操作的特性,读操作将获取与节点相关的数据,写操作将替换节点上的数据

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

  1. 临时节点:临时节点的生命周期取决于过期时间,在临时节点过期后系统会自动删除该节点,临时节点不允许拥有子节点
  2. 永久节点:永久节点的数据会一直被存储,直到用户调用接口将其数据删除,一般用于存储一些永久性的配置信息

Znode 的节点监控:在ZooKeeper 的每个节点上都有一个 Watch 用于监控节点数据的变化,当节点状态发生改变时会触发 Watch 所对应的操作。在 Watch 被触发时 ZooKeeper 会向监控该节点的客户端发送一条通知,说明节点的变化情况

Zookeeper 应用场景

  1. 统一命名服务:在分布式环境下,应用程序经常需要对服务进行统一命名,以便识别不同的服务和快速获取服务列表,应用程序可以将服务名称和服务地址信息维护在 ZooKeeper 上,客户端通过 ZooKeeper 获取可用服务列表
  2. 配置管理:在分布式环境下,应用程序可以将配置文件统一在 ZooKeeper 管理。配置信息可以按照系统配置、告警配置、业务开关配置、业务值配置等分类存储在不同的 Znode 上,各个服务在启动时从 ZooKeeper 读取配置,同时监听各个节点的 Znode,一旦 Znode 中的配置被修改,Zookeeper 就将通知各个服务更新配置
  3. 集群管理:在分布式环境下,实时管理每个服务的状态是 ZooKeeper 使用最广泛的场景
  4. 分布式通知协调:基于 Znode 的临时节点和 Watch 特性,应用程序可以很容易地实现一个分布式通知协调系统。比如在集群中为每个服务都创建一个周期为 30s 的临时节点作为服务状态监控,要求各个服务每 10s 定时向 ZooKeeper 汇报监控状态。当 ZooKeeper 连续 30s 未收到服务的状态反馈时,则可以认为该服务异常,将其从服务列表中移除,同时将该结果通知到监控该节点状态的服务
  5. 分布式锁:由于 ZooKeeper 是强一致性的,所以在多个客户端同时在 ZooKeeper 创建相同的 Znode 时,只能有一个创建成功。基于该机制,应用程序可以实现锁的独占性,当多个客户端同时在 ZooKeeper 创建相同的 Znode 时,创建成功的那个客户端将得到锁,其他客户端则等待