nginx a/b测试需实现“稳分”:同一用户始终命中同一版本,关键在于用稳定标识(如$cookie_uid)哈希分流、整数区间精确控比(如700000 "v1"; 1000000 "v2";)、proxy_pass显式路由至独立upstream、透传x-ab-group标头,并支持url强控与毫秒级降级。

要用 Nginx 的 split_clients 模块实现线上生产环境的业务无感 A/B 测试,关键不是“随机分”,而是“稳分”——同一用户每次请求都命中同一版本,前端不跳变、后端不感知、运维不干预,整个过程对用户完全透明。
选对分流标识,确保用户会话稳定
分流必须基于能长期稳定识别用户的字段,否则同一人刷新页面就进不同版本,测试失效,体验断裂:
- 优先用业务已有的登录态标识,比如
$cookie_uid;若为空,加默认后缀兜底:"${cookie_uid}000",避免空字符串哈希坍缩 - 未登录场景可用
$http_x_real_ip(需配合set_real_ip_from正确配置可信代理网段),比$remote_addr更可靠 - 慎用
$remote_addr单独作为键:NAT 或 CDN 后大量用户共 IP,会导致流量倾斜甚至全进一组 - 如需更高离散度,可拼接:
"${cookie_uid}${http_user_agent}",但注意长度和特殊字符影响哈希一致性
用整数区间精确控制比例,杜绝浮点陷阱
split_clients 不支持小数权重,所有百分比必须换算为 0–999999 区间内的整数边界,否则会被截断导致比例严重失真:
- 70% / 30% 写法正确:
700000 "v1"; 1000000 "v2"; - 错误写法:
0.7 "v1"; 1.0 "v2";→ 实际变成0 "v1"; 1 "v2";,100% 走 v1 - 三组实验(50%/30%/20%):写成
500000 "v1"; 800000 "v2"; 1000000 "v3"; - 上线前务必用小流量抽样验证:在 access_log 中统计
$variant出现频次,确认实际分布与预期一致
与 upstream 解耦路由,避免逻辑耦合
分流变量只负责标记,转发必须由 proxy_pass 显式指向对应 upstream,不能依赖路径重写或隐式拼接:
- 定义独立 upstream 块:
upstream backend_v1 { server 10.0.1.10:8080; }和upstream backend_v2 { server 10.0.1.11:8080; } - 在 location 中直接使用变量:
proxy_pass http://backend_$variant;(结尾不能带斜杠) - 透传关键标头:
proxy_set_header X-AB-Group $variant;,供后端记录日志、做链路追踪或灰度降级 - 静态资源或 CDN 回源路径也需保持 variant 透传,确保前端 JS 可读取当前分组,用于埋点或条件渲染
内置运行时开关,保障灰度可控可退
生产环境不能靠 reload 配置来切流,必须支持毫秒级强制指定和兜底降级:
- 用
map指令覆盖split_clients结果,例如通过共享内存字典或外部配置中心动态下发开关值 - 支持 URL 参数强控:
if ($args ~* "ab_force=v2") { set $variant "v2"; },便于运营临时拉群验证 - 记录完整上下文到独立日志:包括
$variant、原始标识($cookie_uid或$http_x_real_ip)、时间戳、URI,方便归因分析 - 监控必须覆盖各分组的 QPS、延迟、错误率、转化率,发现异常可立即切回主干
不复杂但容易忽略。真正让 A/B 测试“无感”的,不是功能有没有,而是每次请求是否真的落在同一个桶里、每个桶是否真的按你写的数字在跑、出问题时能不能一秒钟切走。











