一批实例同时重启重新注册,etcd 的 watch 会被推多少事件?watcher 数、key 的设计怎么影响?所有客户端同时重连时又是什么压力?这篇用单节点 etcd 3.7.2 逐项量出来。
watcher 数 W | put 延迟 p50 / p99 | 1000 个 put 写完用时 | 全部 watcher 收齐 1000 个事件用时 | 收到的事件总数 | etcd 内存变化 ------------+------------------+-----------------+--------------------------+---------+---------- 0 | 4.3 / 16.9 ms | 0.64 s | - | 0 | +8 MB 50 | 6.5 / 13.9 ms | 0.86 s | 0.86 s | 50,000 | +11 MB 200 | 11.7 / 27.8 ms | 1.64 s | 1.64 s | 200,000 | +20 MB 500 | 29.3 / 78.1 ms | 4.15 s | 4.15 s | 500,000 | +34 MB 事件总数 = 1000 × W:同样的 1000 次注册,watcher 越多,etcd 要推送的事件越多。put 的延迟和“所有 watcher 都收齐”的用时都随 W 增长(看上表),etcd 内存也涨;但要注意 watcher 的读线程(Python)、写入线程和 etcd 都在同一台机器上抢 CPU,延迟增长里有多少来自 etcd 的推送、多少来自测量环境本身,这个实验分不开
事件总数 = 变更数 × watcher 数;延迟、内存都随 watcher 数增长。
做法 | 收到的事件总数 | 收齐用时 | etcd 内存变化 | put 延迟 p99 ----------+---------+--------+-----------+----------- (a) 全局前缀 | 200,000 | 1.65 s | +0 MB | 30.2 ms (b) 按服务前缀 | 10,000 | 0.59 s | +0 MB | 11.8 ms 按服务前缀分片之后,每个 watcher 只收到它关心的那 50 个事件,事件总数是全局前缀的二十分之一;服务发现的 key 设计成 /服务名/实例 而不是平铺在一个前缀下,就是为了让 watcher 只订阅自己用到的服务
按服务分前缀,让每个 watcher 只收到自己关心的事件。
情形 | 补发的事件总数 | 全部补齐用时 | etcd 内存变化 ----------------------+---------+--------+---------- 200 个 watcher 同时重连并补发 | 200,000 | 0.21 s | +110 MB 重连时从旧 revision 补发历史事件,每个 watcher 要补 1000 个事件,200 个同时重连就是 20 万个事件一起补(看上表的用时和内存变化)。注册中心恢复后所有客户端同时重连是 watch 压力最大的时刻之一,客户端重连要加随机退避,避免同时涌入(见第 3 篇)。第二节里“etcd 内存变化”几乎为 0,是因为第一节已经把 etcd 的内存撑起来了,该列只在第一、三节有参考意义
同时重连并补发历史事件占用较多内存;客户端重连要加随机退避。