bigywater.tech
微服务 · 88

流量镜像:影子流量的用法

想用线上真实流量验证新版本又不想影响用户,流量镜像是怎么工作的?shadow 慢了或挂了会不会拖累用户?镜像的写请求会怎样?这篇用 Envoy 1.39.3 的 request_mirror_policies 和本机后端逐项量出来。

100.0%
shadow 慢 1 s、返回 500 或整个停止时,客户端的成功率(五种场景都是这个值)
15 / 1200
shadow 集群 max_connections 5 时,实际到达 shadow 的镜像请求
100 次
镜像 100% 时,100 个 POST 在 shadow 里再执行的写次数

镜像做了什么

实测 · 本机执行输出
  配置                                      | 客户端请求数 | 客户端拿到的响应体   | 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。

shadow 出问题

实测 · 本机执行输出
  场景              | 请求数 | 客户端成功率 | 延迟 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 按请求随机抽样,实际比例围绕设定值波动。

shadow 的熔断器

实测 · 本机执行输出
  配置                                           | 请求数  | 客户端成功率 | 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 必须有隔离的数据与下游。

分步演示

流量镜像
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.8 / Go / nginx / Envoy / etcd / Consul / Nacos / OpenTelemetry / Prometheus(按各篇注明用到哪些及版本);所有服务都是本机自己起的进程,端口 9150 以上,只监听 127.0.0.1,不连任何外部服务,不碰本机已有的实例;故障用 kill -9 进程、让连接断开或在代码里注入延迟与错误来制造;计时用 time.perf_counter,数字每次运行会有小幅抖动。Envoy 1.39.3(--concurrency 1),request_mirror_policies 与 runtime_fraction 的静态配置;prod 与 shadow 是本机的 Python 服务;每种场景只跑了一次,毫秒数每次运行略有不同(例如 runtime_fraction 10% 的实际比例在两次运行里是 11.4% 和 11.6%);镜像流量在 shadow 一侧的计数用 Python 服务自己统计
    • 没有验证:镜像请求的响应对比(diff)工具;镜像对 prod 一侧资源(Envoy 的 CPU、连接数)的额外开销;Istio 的 mirror 与 mirrorPercent 配置;shadow 返回 500 以外的错误类型对 Envoy 统计的影响;镜像 HTTP/2 或 gRPC 流式请求;镜像请求体很大时的缓冲行为;shadow 写真实数据库时的数据污染(本篇 shadow 只是计数器)
    京ICP备2026067502号-1京公网安备11010502063487号