zookeeper集群必须采用奇数节点部署,3节点是生产环境最小可行规模。因其可容忍1台宕机,满足多数派(quorum)要求,而单节点仅适用于测试、双节点无法形成法定多数,5节点及以上则运维成本陡增、收益递减。

zookeeper集群必须用奇数节点,3台是生产最小可行规模
单节点 zookeeper 纯属测试玩具,挂了整个锁系统就崩;双节点无法达成多数派(2/2=100%,但故障时只剩1节点,无法形成 quorum);3 节点可容忍 1 台宕机,5 节点可容忍 2 台,7 节点运维成本陡增且收益递减。实际部署中,3 是平衡可用性、性能与复杂度的硬性起点。
每个节点需独立配置 myid 文件(内容为 1、2 或 3),并确保 zoo.cfg 中列出全部节点:
tickTime=2000 initLimit=10 syncLimit=5 dataDir=/var/lib/zookeeper clientPort=2181 server.1=zoo1:2888:3888 server.2=zoo2:2888:3888 server.3=zoo3:2888:3888
注意:2888 是 follower 与 leader 通信端口,3888 是选举端口,两者都不能被防火墙拦截;DNS 或 hosts 必须能双向解析 zoo1/zoo2/zoo3。
go-zookeeper 客户端连接必须启用重试 + 自动重连
zk.Conn 默认不自动重连,网络抖动或 leader 切换时会直接返回 zk.ErrConnectionClosed 或 zk.ErrSessionExpired,而你的 Lock() 调用可能卡在阻塞等待里——这不是 bug,是设计使然。
正确做法是封装一个带重试逻辑的连接管理器:
- 用
zk.Connect()初始化时传入足够长的超时(如5 * time.Second),避免 DNS 卡住 - 监听
conn.Event()通道,捕获zk.EventSession类型事件,一旦收到StateExpired就主动重建连接 - 所有锁操作前检查
conn.State() == zk.StateHasSession,否则先重连再重试 - 不要在 Watcher 回调里直接调
lock.Lock(),应发 signal 到外部 goroutine 控制重试节奏,防止雪崩
zookeeper分布式锁必须用临时顺序节点,不能用普通临时节点
很多新手误以为“临时节点 + watch”就够了,结果出现多个客户端同时认为自己拿到锁——因为没做序号比对,违反了公平性前提。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
ZooKeeper 锁的核心在于节点命名和监听逻辑:
- 根路径(如
/locks/order)必须是已存在的持久节点,否则go-zookeeper的Lock构造会 panic - 每个客户端创建的子节点名格式必须为
lock-+ 顺序后缀(如lock-0000000001),由conn.CreateProtectedEphemeralSequential()自动生成 - 未获锁者只能监听紧邻前一个序号节点(例如
lock-0000000002监听lock-0000000001),不能监听父路径,否则触发“羊群效应” - 释放锁只需关闭 client 连接或让 session 过期,ZooKeeper 自动删临时节点,无需手动
Delete()
go-zookeeper 的 Lock 结构体不支持续期,别试图 hack
go-zookeeper/zk 库里的 Lock 类型只做一次抢锁,没有内置 lease 续期机制。它依赖 ZooKeeper Session 的 timeout 参数保活(默认 30s),但 Go 客户端不会自动发送 ping 包维持 session。
这意味着:如果锁持有者执行任务超过 session timeout(比如耗时 45s 的数据库迁移),ZooKeeper 会提前回收节点,其他客户端趁虚而入,造成并发冲突。
解决方案只有两个:
- 调大初始化时的
sessionTimeoutMs(如设为60000ms),但上限受 ZooKeeper 集群配置maxSessionTimeout限制 - 在业务逻辑中主动调
conn.Sync()或发空请求(如conn.Exists("/zookeeper"))来刷新 session 时间戳——这不是标准用法,但实测有效
真正需要长时间持锁的场景,ZooKeeper 其实不是最优选;etcd 的 lease + keepalive 更适合,而 Redis 则靠 Lua 脚本+后台 goroutine 续期。ZooKeeper 的优势只在“你确定锁持有时间可控、且必须强公平”时才成立。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










