> 技术文档 > zookeeper介绍

zookeeper介绍


ZooKeeper 概述

什么是 ZooKeeper?

ZooKeeper 是 Apache 基金会开源的一个高性能、高可用、强一致性的分布式协调服务。它主要解决的是分布式系统中的数据一致性问题和状态同步问题。在大型分布式系统中,服务节点会频繁上下线,存在网络延迟、节点宕机、数据不一致等诸多问题。如果没有一个“统一协调者”,各个服务之间将无法有效协作。

ZooKeeper 设计为一个轻量级、高可靠性的“协调中心”,主要用于:服务注册与发现、配置管理、分布式锁、主节点选举等场景。它本身是一个小型的分布式文件系统,但这个文件系统的设计目标不是高并发读写,而是快速、准确的状态通知与元数据维护。

简单来说,ZooKeeper 是分布式系统的“指挥中心”,它不提供计算能力,不负责复杂业务,而是保证各个服务节点“彼此了解彼此”的状态。

ZooKeeper 的主要功能?

1.命名服务(Name Service)

在分布式架构中,服务的地址、端口等信息可能动态变化。如果服务之间直接通过硬编码的 IP 地址通信,会导致部署和扩展极其困难。

ZooKeeper 通过树形节点结构,为每个服务提供唯一的命名路径,服务注册到这个路径下,其他客户端可以通过路径发现最新的服务地址。

/services/user-service -> 192.168.1.10:8080/services/order-service -> 192.168.1.11:8080

这样,服务消费者只需要知道路径 /services/user-service,而不用关心实际 IP 地址是否变化。

2.配置管理

ZooKeeper 可以集中管理分布式系统中的配置文件,所有客户端读取的都是 ZooKeeper 上的配置,且支持动态监听配置变更。

/config/max_connections -> 5000/config/timeout -> 3000ms

当配置更新时,ZooKeeper 会通过 Watcher 机制自动通知所有客户端,不需要手动同步,也不需要重启。

3.分布式锁

ZooKeeper 利用顺序临时节点可以简单高效地实现分布式锁。

实现流程:

  • 每个客户端创建 /lock/lock-XXXX 顺序节点

  • 谁创建的节点编号最小,谁获得锁

  • 如果锁节点被删除,后续节点自动被唤醒

优点:

  • 实现简单,无需复杂同步算法

  • 自动释放(客户端断连,节点自动清除)

  • 天然支持公平锁(按顺序排队)

4.主节点选取(Master Election)

在很多分布式系统中(如 HDFS、Kafka),需要有一个 Master 节点,负责核心业务。ZooKeeper 提供高可靠的主节点选举能力。

选举原理:

  • 所有节点创建临时顺序节点

  • 最小编号的节点成为 Master

  • Master 挂了,ZooKeeper 自动通知下一节点接管

优点:

  • 自动选主,容灾能力强

  • 节点上下线自动感知

  • 选举过程高效可靠

ZooKeeper 的典型应用场景?

场景 说明 服务注册与发现 动态管理服务地址,客户端实时感知 分布式锁 保证任务串行执行,防止并发冲突 配置中心 多节点配置同步,实时生效 Master 选举 高可用主备切换,节点自管理 分布式队列 实现先进先出任务队列

ZooKeeper 已广泛应用于 HDFS、Kafka、HBase、Dubbo、gRPC 等分布式系统,是现代微服务架构的重要基础组件。

Zookeeper架构与部署

单机模式 vs 集群模式

ZooKeeper 支持单机模式和集群模式两种部署方式,适用于不同场景需求:

模式 特点 适用场景 单机模式 无需选举,部署简单,服务单点;写入、读取请求由唯一节点处理 开发环境、功能测试 集群模式 主从架构,支持选举、支持容错;可水平扩展,具备高可用性 生产环境

集群规模与容错能力

ZooKeeper 要求集群节点数量为奇数,以确保能够选出多数节点

节点数量 可容忍宕机数 最小生存节点数 1 台 0 台 1 3 台 1 台 2 5 台 2 台 3

最大容错数 = (n - 1) / 2,其中 n 是集群节点总数。

  • 生产环境建议至少 3 个节点,5 个更稳定。

  • 若需高并发读请求,可加入若干 Observer 节点,提升读性能但不影响一致性。

  • 所有集群节点必须配置一致的 zoo.cfg 配置文件中的 server 列表。

ZooKeeper 集群架构

ZooKeeper 集群使用经典的主从架构(Leader-Follower 架构),为了提高系统可扩展性和读性能,还引入了Observer(观察者)节点。整个集群以一个 Leader 为核心,配合多个 Follower 与 Observer 节点协同工作。

节点类型及职责划分

节点类型 角色描述 Leader 负责处理所有客户端写请求(事务性操作)、发起提议(Proposal)、收集投票并提交事务。 Follower 参与投票选举 Leader,响应客户端读请求,将写请求转发给 Leader,并参与 Proposal 确认过程。 Observer 只参与读操作,不参与 Leader 选举及 Proposal 投票过程,用于扩展系统读能力,不影响一致性。

ZooKeeper 的配置文件解析(zoo.cfg

ZooKeeper 的运行依赖一个名为 zoo.cfg 的配置文件,位于其安装目录下的 conf/ 目录中(通常路径为 conf/zoo.cfg),该文件控制着 ZooKeeper 的核心运行参数,如数据目录、端口号、会话超时时间,以及集群节点信息等。

配置文件的基本结构

zoo.cfg 是一个标准的键值对(key=value)格式的文本文件,内容结构简单,注释使用 # 开头。

tickTime=2000initLimit=10syncLimit=5dataDir=/var/lib/zookeeper/datadataLogDir=/var/lib/zookeeper/logclientPort=2181server.1=192.168.1.1:2888:3888server.2=192.168.1.2:2888:3888server.3=192.168.1.3:2888:3888

关键配置项解释

基础配置项(单机或集群通用)

参数 含义 tickTime 心跳基准时间(单位:毫秒)。ZK 使用它作为会话超时、心跳等的时间单位。建议保持默认:2000ms。 initLimit Follower 与 Leader 初始连接时允许的最大心跳周期数量。限制了 Leader 等待 Follower 同步的时间,超过视为失败。 syncLimit Leader 与 Follower 之间同步数据的最长心跳周期数,超过则认为该 Follower 不可用。 dataDir ZooKeeper 存储内存快照和事务日志的目录。目录下应有一个名为 myid 的文件(集群模式必备)。 dataLogDir 事务日志的单独目录(可选)。若未设置,事务日志也写入 dataDir。分离可提高性能。 clientPort ZooKeeper 对客户端开放的监听端口,默认 2181。客户端通过此端口进行连接和访问。

集群配置项(分布式部署专用)

参数格式 含义 server.X=A:B:C 定义一个集群节点,其中 X 是 myid,A 是主机 IP 或域名,B 是选举通信端口,C 是数据同步端口。

server.1=192.168.1.1:2888:3888

  • server.1:该节点的 myid 是 1;

  • 192.168.1.1:该节点的主机地址;

  • 2888:Follower 用于与 Leader 建立连接的端口;

  • 3888:用于 Leader 选举的通信端口。

每个参与选举的节点都必须在配置文件中显式列出所有集群成员。

myid 文件说明(集群部署必备)

对于每个集群节点,ZooKeeper 要求在 dataDir 目录中存在一个名为 myid 的文件,内容是该节点的 ID(整数),用于标识 server.X 中的 X。

  • dataDir=/var/lib/zookeeper/data

  • 在该目录下需创建文件 /var/lib/zookeeper/data/myid,文件内容为 1,表示这是 server.1

Linux环境下的Zookeeper安装与部署

①安装java环境

sudo apt updatesudo apt install default-jdkjava -version

②下载安装 ZooKeeper

下载官方二进制包

Apache ZooKeeper

cd /optsudo wget https://downloads.apache.org/zookeeper/zookeeper-3.8.4/apache-zookeeper-3.8.4-bin.tar.gzsudo tar -xzf apache-zookeeper-3.8.4-bin.tar.gzsudo mv apache-zookeeper-3.8.4-bin zookeeper

③创建数据目录和日志目录

ZooKeeper 默认需要 dataDir 保存其事务日志和内存快照:

sudo mkdir -p /opt/zookeeper/datasudo mkdir -p /opt/zookeeper/logssudo chown -R $USER:$USER /opt/zookeeper

④配置 ZooKeeper

进入 ZooKeeper 目录,复制配置模板:

cd /opt/zookeeper/confcp zoo_sample.cfg zoo.cfg

编辑 zoo.cfg 文件:

nano zoo.cfg

⑤启动 ZooKeeper 服务端

进入 ZooKeeper 根目录:

cd /opt/zookeeperbin/zkServer.sh start

查看状态:

bin/zkServer.sh status

输出示例:

ZooKeeper JMX enabled by defaultUsing config: /opt/zookeeper/bin/../conf/zoo.cfgMode: standalone

⑥验证连接

你可以用自带客户端连接:

bin/zkCli.sh -server 127.0.0.1:2181

连接后可以尝试:

create /testnode \"hello\"get /testnode

常用命令汇总

服务端管理命令

命令 说明 zkServer.sh start 启动 ZooKeeper 服务 zkServer.sh stop 停止 ZooKeeper 服务 zkServer.sh restart 重启 ZooKeeper 服务 zkServer.sh status 查看 ZooKeeper 当前状态

客户端启动命令

命令 说明 zkCli.sh 启动命令行客户端,默认连接 localhost:2181 zkCli.sh -server ip:port 启动客户端并连接指定的 ZooKeeper 地址(远程/单节点) zkCli.sh -server ip1:port1,ip2:port2,... 启动客户端并连接多个节点地址(集群模式)

客户端交互命令(在 zkCli.sh 中使用)

命令 说明 help 查看所有可用命令 ls /path 列出指定节点下的子节点 create 创建一个节点并写入数据 get 获取指定节点的数据 set 修改指定节点的数据 delete 删除指定节点 stat 查看节点状态信息(如版本、时间戳等元数据) exists 判断节点是否存在 connect 动态连接到其他 ZooKeeper 服务地址 quit 退出客户端

ZooKeeper基本原理

ZooKeeper 数据模型(ZNode)

ZooKeeper 提供树形数据结构,每个节点叫 ZNode,类似 Linux 文件系统。

特性:

  • 节点路径全局唯一

  • 节点存储小数据(最大 1MB)

  • 节点支持版本控制

  • 支持四种节点类型

类型 说明 持久节点 节点创建后永久存在 临时节点 会话断开节点自动删除 顺序节点 节点路径自动追加编号 临时顺序节点 会话断开自动删除且带编号

树形结构示意:

/├── config│ ├── db -> mysql://192.168.1.10│ └── timeout -> 3000├── services│ ├── user-service -> 192.168.1.20│ └── order-service -> 192.168.1.21└── locks ├── lock-00000001 └── lock-00000002

会话机制与临时节点

ZooKeeper 的核心设计理念之一是会话机制(Session),它在客户端和 ZooKeeper 服务端之间维护了一个状态连接,并为分布式系统中的一致性和资源管理提供了基础保障。

会话:

会话是客户端与 ZooKeeper 服务端之间建立的长期连接,基于 TCP 连接并维持心跳(心跳机制是指客户端周期性向服务器发送心跳包,证明自己仍在线)。每个会话有以下关键属性:

  • 会话 ID(sessionId):ZooKeeper 生成的唯一标识符,用于区分不同客户端会话。

  • 会话超时时间(sessionTimeout):客户端与服务端约定的超时时间,默认 30000ms(30秒),指客户端在这段时间内未发送心跳,服务器视为客户端失联。

  • 会话状态

    • 连接中(CONNECTED):客户端已成功与服务器建立会话。

    • 断开连接(DISCONNECTED):客户端和服务器连接暂时中断,客户端可尝试重连。

    • 重连中(RECONNECTED):客户端断线后短时间内恢复连接,保留会话信息。

会话生命周期:

会话的生命周期决定了 ZooKeeper 如何管理客户端资源,尤其是临时节点(Ephemeral Node) 的创建和删除。

生命周期阶段 说明 影响 客户端正常断开 客户端主动关闭连接,发送会话结束通知给 ZooKeeper。 会话关闭,所有该会话创建的临时节点立即被删除。 客户端异常断连 由于网络抖动或宕机导致连接断开,客户端未通知服务器。 服务器开始计时,会话超时后关闭会话,删除对应临时节点。 客户端重连 断线后客户端在会话超时前重新连接,恢复之前的会话状态。 会话继续有效,临时节点依然存在,客户端可以继续操作。

会话与临时节点的关系:

临时节点是会话的产物,具有强绑定性:

  • 临时节点由某个会话创建。

  • 会话关闭(正常或异常超时),其对应的所有临时节点都会被 ZooKeeper 自动删除。

  • 这确保了分布式系统中“会话绑定资源自动释放”的特性,避免资源泄漏。

临时节点的典型应用场景:

  • 分布式锁

    通过客户端创建临时节点来表示锁的持有。只有会话存活,临时节点存在,锁才有效;会话失效,临时节点自动删除,锁释放。

  • 主节点选举

    选举过程中,候选者创建临时顺序节点;节点存在即表示候选者活跃,会话过期则自动放弃竞选,系统可以重新选举。

  • 资源绑定和自动释放

    会话绑定的临时节点可用来表示客户端的资源状态,客户端断开,资源自动回收,确保系统一致性。

ZAB 协议与一致性保障

在分布式系统中,一致性(Consistency)是核心挑战之一。ZooKeeper 作为一个强一致性(CP 模型)系统,依靠其自研的ZAB 协议(ZooKeeper Atomic Broadcast) 来保证多个副本之间的数据一致。ZAB 协议专为 ZooKeeper 构建,具有高效、高可用、高一致性的特性,能够确保即使在节点崩溃、网络分区等复杂环境下,也能提供原子广播与顺序一致性。

ZAB 协议的流程(写操作)

Client → Follower → Leader → Proposal → Follower ACK → Commit
  • 客户端发起写请求

    • 客户端将写请求发送给任意一个 ZooKeeper 服务器(通常是本地最近的 Follower)。

  • 请求转发至 Leader

    • Follower 将请求转发给当前集群中的 Leader 节点,由 Leader 统一处理所有写请求。

  • Leader 生成 Proposal(提案)

    • Leader 为该请求生成一个事务提案,标记一个全局唯一的 事务 ID(zxid)

    • zxid(ZooKeeper Transaction ID)是一个 64 位的编号,前 32 位为 epoch(Leader 选举周期),后 32 位为事务计数器,确保全局有序。

  • Proposal 广播至所有 Follower

    • Leader 将生成的 Proposal 广播给所有 Follower,等待它们的确认(ACK)。

  • 半数以上 Follower 返回 ACK

    • Leader 收到超过半数节点的 ACK 后,认为该 Proposal 达成共识。

  • Leader 发送 Commit

    • Leader 向所有节点广播 Commit 指令,所有节点应用该事务,事务提交成功。

  • 客户端接收到响应

    • 一旦写请求被 Commit,ZooKeeper 才会向客户端返回成功结果,保证事务已被大多数节点写入。

ZAB 的两种工作模式

  • 恢复模式(Recovery Mode)

    • 用于集群启动或 Leader 重新选举期间。

    • 系统会从各节点同步日志,选出最新的 Leader,确保状态最大化一致。

    • 一旦完成同步,切换至广播模式。

  • 广播模式(Broadcast Mode)

    • 正常运行状态下,处理客户端请求。

    • Leader 负责生成 Proposal,分发和提交事务。

Leader 选举机制

在以下两种场景中,ZooKeeper 会触发 Leader 选举:

  • 集群首次启动

  • 现有 Leader 崩溃或不可达

ZooKeeper 使用 ZAB 协议的恢复模式(ZAB Recovery Mode)进行 Leader 选举,确保系统始终拥有一个最新数据状态的 Leader 节点。

选举过程

  • 所有节点启动时进入LOOKING(寻找) 状态,自我推荐为 Leader 候选人。

  • 节点之间互相发送投票(vote),每次投票包含:

    • 节点 ID

    • 最新事务 ID(zxid)

  • 投票策略如下:

    • 优先选择zxid 最大的节点;

    • 如果 zxid 相同,选择myid 最大的节点;

    • 注意:zxid是由leader产生的,并且会广播给所有follower,但是每个follower的zxid可能是不一样的,因为节点日志更新是异步的:某个 Follower 网络慢、重启晚,可能还没收到最新几个 Proposal/Commit,它的 zxid 就会落后。

    • myid 是每个 ZooKeeper 节点的唯一标识编号,在集群部署中由管理员手动分配,通常是一个正整数。

  • 一旦某个节点获得超过半数节点的认可(quorum),则被选为 Leader,其它节点状态切换为 FOLLOWING/OBSERVING。

  • 选出的 Leader 会与所有节点进行数据对齐(同步日志)后,进入广播模式,正式对外服务。

Watch 机制(监听通知)

ZooKeeper 的 Watcher(监听器)机制是其最具特色的功能之一,为分布式系统提供了轻量级的事件通知能力,允许客户端在指定节点上注册监听,一旦节点状态发生变化,ZooKeeper 会异步通知客户端。Watch 机制非常适合用于配置变更通知、子节点变更感知等场景,是构建分布式协调系统的重要基础。

什么是 Watch 机制?

Watcher(监听器)是 ZooKeeper 提供的一种一次性、异步的回调通知机制。客户端可以通过 Watcher 订阅 ZooKeeper 上某个节点的变更事件。当节点发生变化时,ZooKeeper 会主动向客户端推送事件通知。

Watch 支持的事件类型

ZooKeeper Watcher 可以监听以下几类节点状态变化:

监听类型 说明 节点创建 监听目标节点是否被创建 节点删除 监听目标节点是否被删除 节点数据变更 监听目标节点的数据内容是否发生变化 子节点列表变更(非递归) 监听目标节点的直接子节点是否发生变化(子节点被创建或删除)

注意:

  • Watcher 监听不到子节点的内容变化,只能感知子节点的存在状态变化。

  • Watcher 不能递归监听,需要逐层手动注册。

Watch 触发流程

Client → ZooKeeper → 节点变化 → Watch 触发 → 客户端回调执行
  • 客户端注册 Watcher

    • 客户端通过 API(如 zoo_existszoo_getzoo_get_children)向 ZooKeeper 注册监听。

    • 注册时需要传入回调函数,用于后续事件通知。

  • ZooKeeper 记录 Watcher

    • 服务器会将 Watcher 暂存在内存中,等待节点状态变化。

  • 节点发生变化

    • 当节点被创建、删除、数据更新或子节点列表发生变化,ZooKeeper 会查找该节点上的所有 Watcher。

  • 服务器推送事件通知

    • ZooKeeper 向所有注册了该节点 Watch 的客户端发送事件通知。

  • 客户端执行回调函数

    • 客户端收到通知后,立即调用注册的回调函数进行处理(如重新读取节点数据、刷新本地缓存等)。

数据持久化机制

ZooKeeper 采用内存 + 磁盘日志(事务日志和快照)的混合方式进行数据持久化,保证数据的高可用和恢复能力。

内存数据库(Data Tree)

  • ZooKeeper 在运行时会将所有的节点数据(znode)存储在内存中,形成一棵树状结构(Data Tree)。

  • 所有读写操作都是基于这棵内存树进行的,读取速度非常快。

  • 因为是内存数据,如果断电或服务重启,数据会丢失,所以需要持久化机制。

事务日志(Transaction Log)

  • ZooKeeper 会把所有写操作(创建、删除、修改节点等)以事务的形式顺序写入事务日志文件(log.* 文件)。

  • 事务日志是追加写的,不会修改已写入的内容,保证操作的顺序一致。

  • 事务日志的写入保证了数据的持久化和顺序一致性,重启后可以通过日志回放恢复数据。

  • 事务日志通常存储在 ZooKeeper 配置文件中 dataLogDir 指定的目录。

快照文件(Snapshot)

  • 为了避免启动时回放所有事务日志导致恢复时间过长,ZooKeeper 会定期生成快照文件(snapshot.*)。

  • 快照是对内存数据树当前状态的完整拷贝,存储在磁盘上(通常由 dataDir 指定目录)。

  • 在重启时,ZooKeeper 会先加载最新的快照,再依次回放之后的事务日志,快速恢复数据状态。

  • 快照生成频率可以通过配置参数 snapCount 控制(默认是 100000 条事务写入后生成快照)。

持久化流程总结

操作 处理流程 写请求(create/set/delete) 1. 先写入事务日志(保证持久化) 2. 更新内存数据树 读请求 直接读取内存数据树 定期快照 定期将内存数据树写入快照文件,便于快速恢复 服务重启恢复 1. 读取最新快照文件 2. 回放事务日志完成恢复