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=2000
initLimit=10
syncLimit=5
dataDir=/var/lib/zookeeper/data
dataLogDir=/var/lib/zookeeper/log
clientPort=2181
server.1=192.168.1.1:2888:3888
server.2=192.168.1.2:2888:3888
server.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 update
sudo apt install default-jdk
java -version
②下载安装 ZooKeeper
下载官方二进制包
Apache ZooKeeper
cd /opt
sudo wget https://downloads.apache.org/zookeeper/zookeeper-3.8.4/apache-zookeeper-3.8.4-bin.tar.gz
sudo tar -xzf apache-zookeeper-3.8.4-bin.tar.gz
sudo mv apache-zookeeper-3.8.4-bin zookeeper
③创建数据目录和日志目录
ZooKeeper 默认需要 dataDir
保存其事务日志和内存快照:
sudo mkdir -p /opt/zookeeper/data
sudo mkdir -p /opt/zookeeper/logs
sudo chown -R $USER:$USER /opt/zookeeper
④配置 ZooKeeper
进入 ZooKeeper 目录,复制配置模板:
cd /opt/zookeeper/conf
cp zoo_sample.cfg zoo.cfg
编辑 zoo.cfg
文件:
nano zoo.cfg
⑤启动 ZooKeeper 服务端
进入 ZooKeeper 根目录:
cd /opt/zookeeper
bin/zkServer.sh start
查看状态:
bin/zkServer.sh status
输出示例:
ZooKeeper JMX enabled by default
Using config: /opt/zookeeper/bin/../conf/zoo.cfg
Mode: 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 <path> <data> | 创建一个节点并写入数据 |
get <path> | 获取指定节点的数据 |
set <path> <data> | 修改指定节点的数据 |
delete <path> | 删除指定节点 |
stat <path> | 查看节点状态信息(如版本、时间戳等元数据) |
exists <path> | 判断节点是否存在 |
connect <host:port> | 动态连接到其他 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_exists
、zoo_get
、zoo_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. 回放事务日志完成恢复 |