想用线上真实流量验证新版本又不想影响用户,流量镜像是怎么工作的?shadow 慢了或挂了会不会拖累用户?镜像的写请求会怎样?这篇用 Envoy 1.39.3 的 request_mirror_policies 和本机后端逐项量出来。
配置 | 客户端请求数 | 客户端拿到的响应体 | shadow 收到 | shadow 看到的 Host(出现次数)
----------------------------------------+--------+-------------+-----------+--------------------------
默认 | 200 | {prod: 200} | 200 | 127.0.0.1-shadow:9154×200
disable_shadow_host_suffix_append: true | 200 | {prod: 200} | 200 | 127.0.0.1:9157×200
客户端永远只拿到 prod 的响应,shadow 的响应被 Envoy 丢弃;shadow 收到的请求和 prod 一样多;默认情况下 Envoy 会在 Host 的主机名后面追加 "-shadow"(上表是 127.0.0.1-shadow:端口,后缀加在端口之前),这样 shadow 能区分镜像流量;关掉之后 Host 和 prod 看到的完全相同请求复制一份发给 shadow,响应丢弃,客户端只拿到 prod 的响应;shadow 默认看到带 -shadow 后缀的 Host。
场景 | 请求数 | 客户端成功率 | 延迟 p50 (ms) | 延迟 p99 (ms) | Envoy 发往 shadow 的请求(upstream_rq_total) | shadow 收到 ----------------+-----+--------+-------------+-------------+----------------------------------------+---------- 不镜像(对照) | 400 | 100.0% | 1.2 | 2.7 | - | - shadow 正常 | 400 | 100.0% | 1.9 | 3.3 | 400 | 400 shadow 每个请求 1 s | 400 | 100.0% | 1.7 | 3.8 | 400 | 400 shadow 返回 500 | 400 | 100.0% | 1.8 | 3.5 | 400 | 400 shadow 已停止 | 400 | 100.0% | 1.9 | 3.3 | 0 | - 镜像是 “发出去就不管”(fire-and-forget):shadow 慢、报错、或者整个挂掉,客户端的成功率和延迟都和不镜像时在同一个量级,看不出区别
慢、返回 500、整个停止,客户端都不受影响,镜像是发出去就不管。
numerator | 客户端请求数 | shadow 收到 | 占比 ----------+--------+-----------+------- 0% | 1000 | 0 | 0.0% 10% | 1000 | 116 | 11.6% 50% | 1000 | 537 | 53.7% 100% | 1000 | 1000 | 100.0% 镜像比例是按请求随机抽的,实际比例围绕设定值上下波动
runtime_fraction 按请求随机抽样,实际比例围绕设定值波动。
配置 | 请求数 | 客户端成功率 | p50 (ms) | p99 (ms) | 发往 shadow 的请求 | pending_overflow | cx_overflow | shadow 实际收到 ---------------------------------------------+------+--------+----------+----------+---------------+------------------+-------------+------------ (a)默认熔断阈值 | 1200 | 100.0% | 1.0 | 3.3 | 1200 | 0 | 0 | 1200 (b)max_connections 5, max_pending_requests 5 | 1200 | 100.0% | 1.0 | 2.8 | 15 | 1185 | 18 | 15 shadow 的并发被熔断器限住时,超出的镜像请求直接丢掉(这次 1200 个里只有 15 个到了 shadow,其余 1185 个是 pending_overflow),客户端不受影响;代价是 shadow 只看到一部分流量,采样会失真(别以为 shadow 看到的是全量)
shadow 集群的熔断器限住后,超出的镜像请求直接丢掉,shadow 看到的不是全量。
客户端 POST 100 个,响应 Counter({(200, b'paid'): 100});prod 执行的写 100 次,shadow 执行的写 100 次;shadow 收到的请求体长度 [64] 字节(共 100 个)
镜像连请求体一起发,shadow 如果是真实的服务并且连着真实的下游(数据库、消息队列、支付渠道),这些写会再执行一遍;shadow 必须指向隔离的数据和下游,或者只镜像读请求
ERRORS {}镜像连请求体一起发,写请求在 shadow 里真的会再执行一遍,shadow 必须有隔离的数据与下游。