主题
ZooKeeper 监控指标与告警
一句话:ZK 的监控不是看进程活没活,而是盯写路径的 fsync、多数派同步和会话这三条链——它们任一变慢,都会先以「锁拿不到、注册表不更新」的形式在业务侧出现。
适用版本:ZooKeeper 3.8 / 3.9;4lw 行为以 3.5.3+ 为准。指标名与默认值以官方文档为准;文中的阈值只是起点,要按自己的基线校准。
为什么需要
ZK 不在业务请求链路上,故障不会直接报「ZK 挂了」。它先表现为锁等待超时、实例列表不刷新、任务不重新分片;等业务开始重试,ZK 的负载已经被放大了一轮。
只看 ruok 也不够:imok 只说明进程还在,不说明有 quorum,也不说明写延迟正常。需要的是先兆指标——在会话过期和重连风暴之前,就能看到磁盘、请求堆积和副本同步的变化。
核心机制
四个观察面
| 观察面 | 主要指标 | 反映什么 | 恶化后先出什么事 |
|---|---|---|---|
| 写路径 | zk_avg_latency、zk_max_latency、zk_outstanding_requests、zk_last_proposal_size | 一次写从收到到多数派提交的耗时与排队 | 请求堆积、会话心跳延迟 |
| 多数派 | zk_server_state、zk_followers、zk_synced_followers、zk_pending_syncs | Leader 是否在位、Follower 是否跟上 | 选举、写不可用 |
| 会话与 Watch | zk_ephemerals_count、zk_watch_count、zk_num_alive_connections、zk_open_file_descriptor_count | 临时节点、通知、连接和 fd 的规模 | 锁丢失、连接被拒 |
| 资源 | 磁盘使用率、快照/事务日志增长、JVM GC | 写路径的物理上限 | fsync 变慢、进程停顿 |
zk_followers、zk_synced_followers、zk_pending_syncs 只在 Leader 上输出;zk_open_file_descriptor_count 只在 Unix 平台有。采集时要按角色区分,别把「字段缺失」当成 0。
链一:fsync 慢 → 会话过期 → 重连风暴
ZK 的写要等多数派落盘才提交。磁盘 fsync 一变慢,zk_avg_latency 先升,zk_outstanding_requests 开始堆积;请求线程被占住,会话 ping 也被拖慢。延迟超过协商后的会话超时(默认 2×tickTime ~ 20×tickTime,即 4s–40s),服务端就会让会话过期,删除该会话的全部临时节点。
对业务来说,这一刻锁已经丢了、注册信息已经没了。客户端收到 SessionExpired 后会重连、重新注册 Watch、重新 getData/getChildren,所有客户端同时做,就是重连风暴;读请求放大又推高延迟,形成正反馈。
zk_max_latency 是统计窗口内的最大值(重置时机随版本和快照而变),适合诊断,不适合单独告警;告警看 zk_avg_latency 的趋势和 zk_outstanding_requests 是否持续大于 0。zk_last_proposal_size 突然接近 jute.maxbuffer(默认约 1MB)时,说明有超大写入(大 znode 或多 op 事务),同样会拉高 fsync 和网络耗时。
链二:Leader 失联 → 选举窗口 → 依赖方连锁
Leader 失联后,节点进入 LOOKING,写请求被拒绝;依赖 ZK 的锁、注册、选主客户端会一直等到自己的超时。zk_synced_followers < zk_followers 或 zk_pending_syncs 持续大于 0,说明有 Follower 没跟上——这时 Leader 再挂,落后太多的节点不能立刻参选,选举窗口会被拉长。
选举期间所有写都不可用。上游线程池被阻塞的调用占满,超时后重试又把恢复后的 ZK 打高一波。会话超时设得越长,客户端越晚失败,资源被占住的时间也越长;这是「高可用配置」反过来放大故障的典型。
链三:Watch、临时节点、连接泄漏
zk_watch_count 高不一定是泄漏:可能是大量客户端盯着同一个路径,形成羊群效应,节点一变就同时回调。zk_ephemerals_count 持续增长,说明活着的会话多,或者客户端没关闭连接、临时节点没清理。zk_znode_count 无界增长,常见于按业务 ID 建路径却没有回收。
zk_open_file_descriptor_count 接近上限时,新连接建立失败,表现是客户端连不上而不是 ZK 报错。maxClientCnxns=60(默认)限制单个 IP 到单个服务端的连接数,连接泄漏会先打满这个值。
最小上手
在 zoo.cfg 中放行需要的四字命令(3.5.3+ 默认只放行 srvr):
properties
4lw.commands.whitelist=srvr,ruok,mntr,stat,cons,wchs采集与验证:
bash
echo mntr | nc 127.0.0.1 2181 # 指标全集
echo srvr | nc 127.0.0.1 2181 # Mode: leader/follower
echo ruok | nc 127.0.0.1 2181 # 期望 imok,只代表进程在
zkServer.sh status # 依赖 srvr,白名单要保留它Prometheus 侧用 zookeeper_exporter,它把 mntr 转成指标(v0.1.10+ 用 -zk-hosts,默认监听 9141):
bash
./zookeeper-exporter \
-zk-hosts 10.0.0.11:2181,10.0.0.12:2181,10.0.0.13:2181 \
-listen :9141
curl -s localhost:9141/metrics | grep '^zk_'yaml
scrape_configs:
- job_name: zookeeper
static_configs:
- targets: ["zk-exporter:9141"]用 zkCli.sh 做一次端到端确认:
bash
zkCli.sh -server 127.0.0.1:2181
create /monitor/demo value
stat /monitor/demo # 看 cversion、ephemeralOwner、zxid
get -w /monitor/demo
delete /monitor/demo生产动作
以下阈值均为起点,按自己的基线校准。告警要带角色(Leader/Follower)和实例,不要只报集群汇总。
| 告警 | 指标/条件 | 级别 | 先做什么 |
|---|---|---|---|
| 无 quorum | zk_server_state=looking 持续 30s,或全集群 Leader 数为 0 | P0 | 按 zxid 从大到小恢复节点,先凑多数派 |
| Follower 掉队 | zk_synced_followers < zk_followers 持续 5m | P1 | 查掉队节点的磁盘、网络、GC;不要重启 Leader |
| 同步积压 | zk_pending_syncs > 0 持续 5m | P1 | 同上;此时新 Leader 可能落后 |
| 写延迟 | zk_avg_latency 超过基线 2 倍或持续大于 100ms | P1 | 查 fsync、磁盘 await、GC |
| 请求堆积 | zk_outstanding_requests > 0 持续 1m(或大于 10) | P1 | 先查磁盘和网络,不要急着重启 |
| 会话将过期 | zk_max_latency 接近协商超时(如 4s) | P1 | 诊断 GC 和磁盘;不要靠调大 sessionTimeout 掩盖 |
| 磁盘水位 | dataDir/dataLogDir 大于 70% 预警,大于 85% 严重 | P1/P0 | 开自动清理或扩盘;不要删 version-2 |
| 连接/fd | zk_open_file_descriptor_count 大于上限 80%;cons 按 IP 聚合异常 | P1 | 查连接泄漏、单例、优雅关闭 |
| Watch/临时节点 | zk_watch_count 或 zk_ephemerals_count 1 小时增长超过 30% | P2 | 查羊群、未清理路径、会话泄漏 |
| GC | Full GC 停顿大于 1s 或老年代大于 80% | P1 | 调堆、查泄漏;停顿超过会话超时会失锁 |
先分清两种局面:
- 延迟高但仍可用:
zk_server_state是 leader/follower,synced_followers等于followers,写还能成功。此时不要重启任何节点,先限掉非核心写入,查磁盘和 GC。 - 过半不可用:节点停在
LOOKING,写失败。先恢复 quorum,按 zxid 最大的节点先起、一次一台;不要清空dataDir,也不要在 quorum 不足时做清理。
处置顺序:确认 quorum 与角色 → 磁盘/GC → 连接与 Watch → 最后才考虑重启单节点。恢复后要预期会话已经过期,临时节点和锁都没了,业务必须重新注册、重新抢锁;不能假设原来的锁还在。
边界
ZK 的优势是强一致和协调原语:写经 ZAB 多数派提交,顺序有保证;Watch 能感知变更;临时节点天然表达「会话活着才算数」,很适合选主、分布式锁、小规模元数据。Curator 把这些模式封装好了,生态成熟。
代价同样明确:所有写都走 Leader 并等待多数派 fsync,写吞吐受磁盘和网络约束;数据常驻内存,单个 znode 默认约 1MB,不适合放大对象;会话靠心跳维持,GC 停顿、网络抖动都会触发过期和重连;Watch 是一次性的,客户端要自己恢复状态。
所以它不该被当成高频配置中心或大流量注册中心。配置变更频繁、需要推送和本地缓存,优先 Nacos;Kubernetes 生态、需要流式 Watch 和 Raft 运维模型,优先 etcd;需要 DNS 和服务健康检查,考虑 Consul。ZK 适合低频、强一致、数据量小的协调场景;把它放在业务数据面或每秒数万次读的注册场景,代价会远高于收益。
讲清楚
ZK 的告警盯三条链。写路径上,事务日志 fsync 变慢会让
avg_latency和outstanding_requests先升,会话心跳被拖过超时,临时节点被删,锁和注册一起失效,客户端再同时重连,读请求放大,延迟更高。多数派这条链看synced_followers是否少于followers、pending_syncs是否持续大于零;Leader 一挂就进选举,期间不能写,依赖方超时连锁。资源这条链看磁盘只增不减、fd 和 Watch 泄漏。处置时先分清「延迟高但仍有多数派」和「已经 looking」:前者查磁盘和 GC,不要重启;后者先恢复 quorum,按 zxid 从大到小起。ZK 强在顺序一致和临时节点,代价是写受多数派 fsync 约束、会话与重连成本高,所以只放低频协调数据;高频配置和大流量注册交给 Nacos 或 etcd。