etcd选主必须用lease+put+watch机制:各节点独立创建租约并写入带leaseid的leader key,通过watch前缀监听删除事件触发重新选举,需正确处理续租失败、断连重试及revision恢复。

etcd 选主必须用 CompareAndSwap 还是 Lease?
必须用 Lease + Put 带 leaseID + Get 配合 WithRev 或 WithPrefix 判断,不能依赖旧版的 CompareAndSwap(v3 API 已移除)。etcd v3 的选主本质是“租约抢占”:多个节点并发创建带租约的 key(如 /leader/worker-001),谁成功写入且租约未过期,谁就是 leader。
常见错误是直接 Put 不带租约,导致节点宕机后 key 永久残留,新节点永远抢不到主;或者租约续期失败却不主动释放 key,造成脑裂。
- 初始化时创建
Lease,TTL 设为 5–15 秒(太短增加 etcd 压力,太长故障恢复慢) -
Put时指定leaseID,并设WithPrevKV方便判断是否首次写入 - Leader 必须在后台 goroutine 中定期调用
Lease.KeepAlive,收到chan *clientv3.LeaseKeepAliveResponse才算续期成功 - 一旦
KeepAlive返回 error 或 channel 关闭,立即Delete自己的 leader key
如何检测 leader 失效并触发重新选举?
不能轮询 Get 所有候选 key——开销大且无法感知租约自然过期。正确做法是监听 leader key 的删除事件,配合租约 TTL 的被动通知机制。
etcd 的 Watch 对租约关联的 key 具备天然感知能力:当 leader 节点崩溃、未续租或网络分区,其 lease 会自动过期,对应 key 被自动删除,watch channel 会立即收到 EventTypeDelete 事件。
- 所有节点启动时,对固定前缀(如
/leader/)建立长期Watch,用WithPrefix+WithPrevKV - 收到
Delete事件后,检查被删 key 是否是当前已知 leader;若是,立刻尝试Put自己的 leader key - 避免多个节点同时抢——加一个随机 jitter(如 100–500ms)再发起
Put,减少冲突概率 - 不要在 watch 回调里做重试逻辑,watch channel 本身可能阻塞;应把事件发到内部 channel,由单独 goroutine 处理
状态同步该用 watch 还是定时 get?
用 Watch,但必须处理好断连重试和事件丢失边界。etcd watch 是流式、有序、带版本号(Revision)的,只要连接不断,不会丢事件;但网络闪断后,需从断连前的 Revision 继续 watch,否则跳过中间变更。
典型错误是每次重连都从最新 revision 开始 watch,导致状态不同步;或不设 WithProgressNotify,无法感知 long poll 是否卡住。
- 首次 watch 用
clientv3.WithLastRev()获取当前 revision,然后从该 revision + 1 开始监听 - 断连重试时,用上一次收到事件的
Event.Kv.ModRevision + 1作为起始 revision(注意处理ModRevision == 0的初始情况) - 启用
WithProgressNotify,定期收到EventTypeProgress,可据此判断连接健康度 - 状态数据建议存为单个 key(如
/state/config),避免 prefix watch 太宽泛;结构体序列化用json.Marshal,不要用 gob(跨语言不兼容)
为什么 Lease.ID 不能复用?
因为 etcd 的 lease 是引用计数机制:同一个 LeaseID 可绑定多个 key,只要有一个 key 存在,lease 就不会过期。如果多个节点共用一个 lease,A 节点挂了但它的 key 没删干净,B 节点即使拿到 lease 也无法真正成为 leader——key 还挂着,别人也抢不了。
更隐蔽的问题是 lease ID 泄露:比如用固定字符串哈希生成 lease ID,多个实例可能意外复用,导致租约生命周期失控。
- 每个节点必须独立创建自己的
Lease,生命周期与本节点强绑定 - 不要缓存
Lease.ID到本地磁盘或配置,重启后必须新建 lease - 调试时可用
etcdctl lease list和etcdctl lease timetolive --keys [ID]查看活跃 lease 及绑定 key - 如果业务要求“优雅降级”,可在 lease 即将过期前(如剩余 1s)主动
Delete自己的 key,而不是等自动清理











