etcd v3 的 newelection 最稳妥,因其将 lease 创建、cas put、watch 删除、自动重试四步封装为原子操作,底层用事务确保租约与 key 绑定,避免手写漏 withlease 或 watch 断连不重试导致的脑裂、假活或 key 残留。

直接用 clientv3.NewElection 是最省事的路径,但不设 Lease TTL、不处理 KeepAlive 失败、不监听 Election.Observe 而是轮询 Get,十有八九会脑裂或假活。
etcd v3 的 NewElection 为什么比手写 CAS 更可靠
它把 Lease 创建、CAS Put、Watch 删除、自动重试这四件事封装进一个类型里,底层用事务保证「租约绑定 key」原子性。手写容易漏掉 WithLease 或误用 Put 不带 lease,导致节点宕机后 key 永久残留;也容易在 Watch 断连后没重试,错过删除事件。
实操建议:
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 必须传入带 TTL 的
clientv3.Lease实例,TTL 建议 10~15 秒——GC 停顿通常 -
Campaign返回 nil 才算真正当选;非 nil 错误要区分:context.DeadlineExceeded是超时未抢到,clientv3.ErrNoLeader是 etcd 不可用,不能一概重试 - 当选后别存
isLeader bool全局变量,每次业务操作前仍需Get(ctx, leaderKey)校验,防幽灵存活
KeepAlive 失败后必须立刻 Resign,不能等租约自然过期
Lease 过期是 etcd server 单方面行为,客户端无感知。如果 KeepAlive goroutine 因网络抖动或 channel 满而退出,但代码还继续以 Leader 身份发请求,就会双主。
实操建议:
- 启动 KeepAlive 必须用独立 goroutine,并消费返回的
chan *clientv3.LeaseKeepAliveResponse—— 不消费会导致后续续租静默失败 - 一旦从该 channel 收到 error 或 channel 关闭,立即调用
Election.Resign(ctx),并清空本地 leader 状态 - 别用
Lease.TimeToLive轮询判断是否过期:它只返回快照,且频繁调用增加 etcd 压力
监听 Election.Observe 而不是自己 Watch 前缀
Election.Observe 内部已封装带 WithPrefix 和 WithRev 的 watch,能自动从断连点续播,避免事件丢失;自己手写 watch 容易漏掉 WithPrevKV,导致无法区分是新建还是覆盖,也容易在重连时传错 revision。
实操建议:
- 用
Observe获取clientv3.LeaderKey和clientv3.LeaderRev,不要解析 key 名称拼接 - 收到新 leader 通知后,若本节点不是 leader,应停止所有关键业务逻辑(比如定时任务、HTTP handler),而不是仅打日志
- 如果需要响应 leader 变更做清理,把逻辑放在
OnStoppedLeading回调里,别塞进 watch goroutine
Consul 选主必须手动补 session 状态检查,否则必脑裂
Consul 没有原生 election API,靠 session + KV 模拟。但 session 续期是异步的,KV.Put 不支持 CAS,两个节点可能同时写成功。etcd 的 lease 是强绑定的,Consul 的 session 则可能“活着但已失效”。
实操建议:
- 每次执行关键操作前,必须先调
session.Info查 session 状态,返回nil或ErrNotFound就得立即停服 - Put leader key 时加
AcquireSession参数,但别依赖它防并发——它只保证 session 存在,不保证唯一写入 - 避免用
session.Revoke主动销毁,改用session.Destroy配合 TTL 自动回收,减少竞态窗口
最易被忽略的是:etcd 里 leader key 存在 ≠ 当前节点仍是 leader。租约可能已过期但 key 尚未被 server 清理(延迟最多 1 个 tick),此时 Get 能读到值,但业务已不该运行。必须结合 Lease.TimeToLive 查 lease 状态,或直接信任 Observe 流的通知时机。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










