etcd clientv3.Put 写不进主因是连接未建立,表现为 context.DeadlineExceeded 或 rpc error: code = Unavailable;需检查 endpoints 协议端口、使用带超时的 context、显式判 err、TLS 配置及 watch/锁的正确用法。

etcd clientv3.Put 写不进去,十有八九是 context 被 cancel 或 endpoint 连不上
不是代码写错了,而是连接根本没建起来。etcd v3 的 Put 不会抛出“连接失败”这种明确错误,它只返回 context.DeadlineExceeded 或 rpc error: code = Unavailable——这两个信号都在告诉你:client 没连上集群。
- 检查
endpoints是否带协议和端口,比如[]string{"http://127.0.0.1:2379"},而不是"127.0.0.1:2379"(缺协议会静默降级为 http,但若服务端只开 https 就直接失败) - 别用
context.Background()直接调Put,务必加超时:ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) - 必须显式判断
err != nil,哪怕resp不为 nil——失败时resp可能非空但无实际写入 - 如果启用了 TLS,
etcd.Client初始化时必须传tls.Config,哪怕只是跳过校验:&tls.Config{InsecureSkipVerify: true}
watch 配置变更却只触发一次?你没在 goroutine 里持续读 channel
clientv3.Watch 返回的是一个 clientv3.WatchChan,本质是 chan clientv3.WatchResponse。它不会自动拉取、不会重连、更不会帮你消费——你停在 for-range 外面,它就卡死;你没起 goroutine,事件来了也没人收,缓冲区满(默认 100 条)后直接阻塞 server 端。
- 必须立刻起 goroutine:
go func() { for wr := range watchChan { /* 处理 wr.Events */ } }() - watch 默认从“当前 revision + 1”开始监听,如果配置在 watch 启动前就被改过,你就收不到那条变更。正确做法是先
Get一次,拿到resp.Header.Revision,再用clientv3.WithRev(rev)启动 watch - watch 连接断开时,
WatchChan会关闭,但不报错——你要检查ok == false并主动重建 watch - 别把耗时操作(比如 HTTP 请求、DB 写入)塞进 watch 循环里,否则 channel 接收被卡住,后续事件堆积或丢失
读配置用 Get 还是 WithSerializable?看你是要“快”还是要“绝对最新”
etcd 默认的 Get 是线性一致性(linearizable)读,会强制转发到 leader,延迟高但值一定是最新的;加了 clientv3.WithSerializable() 就允许从任意节点读,快很多,但可能读到旧值——对配置类场景完全够用,且更稳。
- 普通配置读取(如数据库地址、超时时间):加
clientv3.WithSerializable(),性能更好,跨机房部署也不易抖动 - 强一致性控制开关(如熔断开关、灰度开关):去掉
WithSerializable(),依赖默认 linearizable 读 -
Get返回的Kvs是切片,key 不存在时为空,别直接取Kvs[0]导致 panic - 别把
Get结果缓存在局部变量里长期复用——etcd 不推变更,得靠自己 watch 或轮询
分布式锁不能用 Put + Delete 模拟,必须用 Lease + Txn
裸 Put 没 TTL,裸 Delete 不校验所有权,谁都能删,锁就形同虚设。真正安全的锁必须绑定租约(Lease),并通过事务(Txn)原子地检查 key 是否已被占用、再写入,释放时也必须用 CAS 校验 value 是否匹配。
立即学习“go语言免费学习笔记(深入)”;
- 加锁流程:调
Grant创建带 TTL 的 lease →Put时用clientv3.WithLease(lease.ID)→ 写入失败说明被占,重试前建议 sleep 避免打爆 - 解锁必须用
Txn:先Get当前 key 值,再用Compare判断 value 是否匹配,匹配才Delete,否则拒绝释放 - 别在锁持有期间做长耗时操作,lease TTL 必须大于业务最大执行时间,否则锁自动过期导致并发冲突
- 锁 key 建议带服务标识前缀,比如
/lock/order-service,避免不同服务误删彼此锁
etcd 的 watch 和锁机制看着简单,但每个环节都卡在细节上:context 生命周期、revision 对齐、lease 续期、Txn 的 Compare 条件——漏掉任何一个,线上就容易出竞态或静默失效。


















