bigywater.tech
ZooKeeper · 98

Go ZooKeeper 客户端

Go 的 ZooKeeper 客户端风格和 kazoo、Curator 很不一样:没有重试策略、没有缓存、watch 是 channel。这篇用 go-zookeeper/zk 实测它的错误值、watch、并发、Multi、自带锁,以及服务端不可用时的行为。

一次性 channel
GetW 的 watch;循环里重新 GetW,200 次更新收到 170 次事件
35.9 倍
64 个 goroutine 并发相对顺序调用的吞吐
立即失败
服务端不可用时 Get 在 0.3–0.7 s 内返回 ErrNoServer

错误与基本调用

实测 · 本机执行输出
  Create 已存在 → zk: node already exists | errors.Is(err, zk.ErrNodeExists) = true
  Get 不存在 → zk: node does not exist
  Set 用旧 version → zk: version conflict
  临时节点下建子节点 → zk: ephemeral nodes may not have children
  FlagSequence 返回的路径 → /g/seq-0000000000 <nil>

watch

实测 · 本机执行输出
  第一次变化收到: EventNodeDataChanged /g
  第二次变化(没有重新 GetW)收到: Unknown (读到零值事件:watch 的 channel 触发一次后就被关闭)
  循环里重新 GetW:连续 Set 200 次,收到 170 次事件;最终值 r199

watch 的一次性语义见这一篇。Go 里每个 watch 是一个 channel,触发一次后关闭。

并发

实测 · 本机执行输出
  顺序 326 个/s(6.13 s);64 goroutine 11709 个/s(0.17 s);35.9 倍

和 kazoo、Curator 一样,流水线化是提高吞吐的关键。

Multi

实测 · 本机执行输出
  Multi(建 /m1, 建已存在的 /m2) → zk: node already exists | /m1 存在 = false
  Multi(建 /m3, 建 /m4) → <nil> | /m3 = true /m4 = true

自带的锁

实测 · 本机执行输出
  最终 200 / 200,用时 1.8 s;/glock 下现在的子节点数 0
  持有锁时的锁节点名: _c_a1570903446f6a2122ed5ffffd14af4f-lock-0000000000

机制与分布式锁那篇相同。

服务端不可用

实测 · 本机执行输出
  0.0s StateConnecting
  0.0s StateConnected
  0.0s StateHasSession
  0.9s StateDisconnected
  0.9s StateConnecting
  1.9s 调用 Get → zk: could not connect to a server(耗时 661 ms)
  1.9s StateConnecting
  2.9s StateConnecting
  2.9s 调用 Get → zk: could not connect to a server(耗时 302 ms)
  3.9s 调用 Get → zk: could not connect to a server(耗时 300 ms)
  3.9s StateConnecting
  4.9s StateConnecting
  5.9s StateConnected
  6.0s StateHasSession
  10.6s 服务端恢复后 Get → <nil>(耗时 6 ms)

Go 客户端在服务端不可用时不阻塞等待而是立刻返回错误,调用方必须自己决定重试(带退避)。不要在没有消费事件 channel 的情况下使用:事件 channel 要有人读(我的程序里单独启了一个 goroutine 读)。我没有测不读事件 channel 会不会阻塞客户端。

分步演示

Go 客户端
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / ZooKeeper 3.9.6(brew 安装的 jar,用 java -cp 直接启动自己的实例,OpenJDK 17 Temurin,JVM 堆 128 MiB)/ 本机回环端口 7300+,数据在临时目录;默认 tickTime=2000;计时用 time.perf_counter,数字每次运行会有小幅抖动。Go 1.27.1(darwin/arm64)+ github.com/go-zookeeper/zk v1.0.4(经代理下载);服务端重启为 SIGTERM 后重新启动
    • 没有验证:不消费事件 channel 时是否阻塞、其它 Go 客户端(如 samuel/go-zookeeper 的各分支)、TLS / SASL、会话过期后的行为细节、zk.Lock 与 Curator 锁的互斥
    京ICP备2026067502号-1京公网安备11010502063487号