bigywater.tech
微服务 · 31

流量分流:按权重、按头、按 cookie,nginx 与 Envoy 的实测分布

灰度的 10% 流量怎么分?按权重、按请求头还是按 cookie?同一个用户会不会一会儿旧版本一会儿新版本?这篇用 nginx 1.31.6 和 Envoy 1.39.3 把几种分流方式量出来。

10.4% / 10.0% / 9.8%
nginx split_clients、nginx upstream weight=9:1、Envoy weighted_clusters 在 10000 个请求里分给新版本的比例
0%
split_clients 用固定字符串当 key 时新版本得到的流量(10% 的权重完全无效)
0 对 176
200 个用户各 20 个请求:按用户哈希分流与 nginx 加权轮询下,两个版本都见到过的用户数

按权重

实测 · 本机执行输出
  做法                                  | S(稳定) | C(新版本) | C 的比例
  ------------------------------------+-------+--------+------
  (a) nginx split_clients $request_id | 8962  | 1038   | 10.4%
  (b) nginx upstream weight=9:1       | 9000  | 1000   | 10.0%
  (c) nginx split_clients 固定 key      | 10000 | 0      | 0.0% 
  Envoy weighted_clusters 90:10       | 9018  | 982    | 9.8% 
  (b) 是确定性的加权轮询,正好 9000 / 1000;(a) 和 Envoy 是按随机数分,有抖动,10000 个请求时标准差约 30;(c) 固定 key 每个请求都落到同一个桶,10% 的权重没有意义,这就是 split_clients 必须选一个随请求(或随用户)变化的 key 的原因

split_clients 随机;upstream weight 是确定性的加权轮询;Envoy 是随机选择簇;key 必须随请求或用户变化。

按头和 cookie

实测 · 本机执行输出
  请求                    | nginx:去 C 的个数(共 20) | Envoy:去 C 的个数(共 20)
  ----------------------+---------------------+--------------------
  无头无 cookie            | 0                   | 0                  
  X-Canary: 1           | 20                  | 20                 
  X-Canary: 0           | 0                   | 0                  
  Cookie: canary=1      | 20                  | 20                 
  Cookie: a=b; canary=1 | 20                  | 20                 
  Cookie: canary=10     | 0                   | 0                  
  Cookie: mycanary=1    | 0                   | 0                  
  相近的 cookie(canary=10、mycanary=1)都没有被误命中:nginx 用 $cookie_canary 按 cookie 名取值再比较,Envoy 对整个 cookie 头写了一个要求 canary=1 前后是分隔符的正则;两者都要把匹配写精确,否则 canary=10 之类的值会被当成 canary=1

两个网关的结果一致,匹配要写精确。

一致性

实测 · 本机执行输出
  做法                               | 只看到 S 的用户 | 只看到 C 的用户 | 两个版本都看到过的用户
  ---------------------------------+-----------+-----------+------------
  nginx split_clients $http_x_user | 179       | 21        | 0          
  nginx upstream weight=9:1        | 24        | 0         | 176        
  Envoy weighted_clusters 90:10    | 22        | 0         | 178        
  按用户(或会话)哈希分流,同一个用户始终落在同一个版本,体验一致,也便于排查;每个请求独立随机的分流,10% 的流量会让约 1 − 0.9^20 = 88% 的用户在 20 个请求内碰到过新版本

按用户哈希分流让同一用户始终落在同一个版本;每个请求独立分流,约 88% 的用户在 20 个请求内碰到过新版本。

分步演示

流量分流
本步的对象

    本文用到的工具与边界

    • 本机 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,数字每次运行会有小幅抖动。nginx 是 Homebrew 的 1.31.6;Envoy 是 Homebrew 的 1.39.3(为本系列 brew install envoy,静态配置,--concurrency 1);后端是本进程里的 Python 服务;cookie 匹配在 Envoy 里用了对整个 cookie 头的 safe_regex;每种配置只跑一轮
    • 没有验证:Envoy 的 runtime 流量分配和 header_hash 一致性;nginx split_clients 的哈希算法(MurmurHash2)在用户数很少时的分布偏差;gRPC / HTTP/2 长连接上的请求级分流;服务网格(Istio)里的 VirtualService 权重
    京ICP备2026067502号-1京公网安备11010502063487号