本轮为理论笔记与静态审读,示例、安装命令和验收器均未执行。下文命令用于解释步骤,预期结果不代表本轮实测;“历史验收”仅指此前批次记录。阅读和书面练习不需要启动服务。
etcd lease、watch 与历史压缩
租约与注册存活
前置:理解 revision 与前缀。lease 把键的存活时间与服务端授予的 TTL 绑定,TTL 是存活检测窗口,不是精准计时器。客户端周期发送 keep-alive,服务端维持租约;过期或 revoke 后附着的键会被删除,并产生 DELETE 事件。一个租约能附多个键,一个键同时只附一个租约。
node examples/etcd/app.mjs register /course/worker worker-A
docker exec showdocs-m4b-etcd-main etcdctl get /course/worker程序申请 3 秒 lease、写注册键、续约一次后退出。立即查询通常有值;稍后查询为空。实际 TTL 可大于请求的最小值,别断言恰好 3000 毫秒删除。长驻服务需持续续约并检测续约失败;这里故意让租约过期,帮助看清注销机制。
注册键存在不等于服务健康、网络一定可达或旧进程已经停止执行。若租约用于分布式锁,旧持有者可能暂停后恢复;外部存储通常还需校验单调 fencing token,避免失去租约的旧持有者继续写入。
Watch 与断线恢复
watch 重连需要保存最后应用的位置。watch 按 revision 观察键的 PUT/DELETE 事件。先做一次完整读取并保存响应 header.revision=N,再从 N+1 开始 watch,可以覆盖“读完到开始监听”之间的变化。先启动 watch 再读快照而不处理边界,容易重复或漏处理;恢复后按完整应用的 revision 去重,并按响应批次保存位置。一个事务可在同一 revision 改多个键,不能处理其中一个事件后便丢弃该 revision 的其余事件;需先完整应用同一 revision 的事件,再持久化检查点。
在终端 A 运行:
docker exec showdocs-m4b-etcd-main etcdctl watch /course/worker -w json终端 B 再运行 register;A 先看到 PUT,租约过期后看到 DELETE。JSON 事件中的 type=1 表示 DELETE;删键时 value 往往为空,若业务需要旧值可选择 prev-kv。watch 是状态变更通知,不是无需保留和确认的永久消息队列。客户端断线应从最后应用 revision+1 继续,而不是从“现在”开始以免漏事件。
Compaction 与重新同步
compaction 丢弃过旧的 MVCC 历史,当前值仍能读。读取或 watch 已压缩 revision 会遇到 mvcc: required revision has been compacted。此时该段历史已不可补发,正确处理是重新全量读取、重建缓存,再从新 header.revision+1 监听;无限重试相同旧 revision 没有用。
docker exec showdocs-m4b-etcd-main etcdctl get /course/config -w json
docker exec showdocs-m4b-etcd-main etcdctl compact 10
docker exec showdocs-m4b-etcd-main etcdctl get /course/config --rev=210 必须换成不超过当前 revision 的实际值,2 必须确实早于压缩边界。最后一条应非零并报 compacted;若请求未来 revision,则是另一个错误。压缩释放历史的逻辑空间,defrag 才将数据库空闲页整理归还文件系统;defrag 会影响成员响应,应按运维窗口和成员顺序处理。
练习与参考解法
题目:客户端断线超过历史保留窗口,缓存仍有旧服务地址;连接恢复后收到 compacted,应删掉全部服务还是继续用旧缓存?
提示:先判断 compacted 之后哪些历史仍可请求,再区分缓存状态与可用性策略。
参考答案与边界
将缓存标记为需要重新同步,读取完整前缀并原子替换本地视图,再从新 revision 监听。是否允许短时间使用旧缓存属于业务可用性策略;必须暴露缓存陈旧状态,不能把“没收到 DELETE”当作服务仍然存活的证据。
继续阅读
- 完整实践
- 知识专题
- 知识专题