服务发现靠 watch 感知变化,断线之后怎么保证不丢事件?历史被压缩掉怎么办?一次事务的多个修改会怎么到达?watcher 多了延迟会怎样?这篇用 etcd 3.7.2 的流式 watch 逐项量出来。
重连方式 | 断开前收到 | 重连后收到 | 整个过程没收到的 key | 重复收到的 key ----------------------------------------+-------+-------+--------------+---------- (a) 不带 start_revision(从现在开始) | 10 | 1 | 50 / 61 | 0 (b) start_revision = 最后看到的 revision + 1 | 10 | 51 | 0 / 61 | 0 从“现在”开始监听时,断开的 2 秒里写入的 50 个事件永远丢了;带上最后看到的 revision + 1 重连,事件一个不丢、也没有重复
带上最后看到的 revision + 1,事件不丢不重。
start_revision | 结果 | 收到的事件数 | 事件的 revision 范围 ------------------------------+------------------------------------------------------+--------+---------------- (a) 旧 revision(263) | 被拒:canceled=True,cancel_reason=,compact_revision=313 | 0 | - (b) 等于 compact revision(313) | 创建成功 | 11 | 313…323 (c) compact revision + 1(314) | 创建成功 | 10 | 314…323 当前 revision 是 323,compact 到了 313。比 compact revision 小的历史取不到,etcd 创建 watch 后立刻把它取消(canceled = true)并带上 compact_revision;客户端遇到这种错误必须重新 list(拿到当前全量和 header.revision),再从 header.revision + 1 开始 watch,中间被压缩掉的变化只能靠 list 的结果补上
start_revision 小于 compact revision 时 watch 被立刻取消,必须重新 list 再 watch。
watch 响应序号 | 事件数 | 事件(类型:key@revision) | 各事件的 prev_kv 旧值 -----------+-----+-------------------------------------------------------+---------------- 1 | 3 | PUT:/tx/a0@324 PUT:/tx/a1@324 PUT:/tx/a2@324 | - - - 2 | 3 | DELETE:/tx/a0@325 DELETE:/tx/a1@325 DELETE:/tx/a2@325 | 1 1 1 3 | 1 | PUT:/tx/z@326 | - 4 | 1 | PUT:/tx/z@327 | old 一次 Txn 提交的 3 个 put 在同一个 revision 里,在同一个 watch 响应里一起到达;删除前缀产生一个 DELETE 事件对应一个 key,也在同一个 revision;prev_kv 让 watcher 在 PUT(第二次写 /tx/z)和 DELETE 事件里拿到修改前的值;第一次写 /tx/z 没有旧值,所以是 “-”
一次提交的多个修改在同一个 revision 和同一个响应里到达,prev_kv 给出旧值。
watcher 数 | 收到的事件总数 | 延迟 p50 | p99 | 最大 ----------+---------+----------+----------+--------- 1 | 30 | 6.58 ms | 9.23 ms | 9.40 ms 50 | 1500 | 7.69 ms | 17.97 ms | 18.63 ms 200 | 6000 | 14.40 ms | 22.11 ms | 22.83 ms
watcher 数增加,延迟缓慢增长,事件不少。