split_clients模块需预处理变量确保非空且一致,如用map构造$routing_key;分组区间须连续覆盖0–99;proxy_pass不支持变量拼接,需配合upstream或lua实现动态路由。

split_clients 模块不支持直接提取 UserID 请求头或 Cookie
nginx 的 split_clients 模块只接受变量作为输入源,且该变量必须在 split_clients 执行前已存在并完成求值。它**不解析请求体、不读取 Cookie、不执行正则提取**——也就是说,你不能在 split_clients 语句里写 $cookie_userid 或 $arg_uid 并期望它自动 fallback 到默认值或做缺失处理。如果变量为空或未定义,哈希结果会不稳定(比如空字符串哈希固定为某值),导致同个 UserID 在不同 worker 上被分到不同 upstream。
- 必须提前用
map或set(配合 lua)把 UserID 归一化为一个确定、非空、跨 worker 一致的字符串 - 推荐用
map:它在 rewrite 阶段早期执行,支持嵌套默认值,且无性能损耗 - 例如,优先取
$arg_uid,其次$cookie_userid,最后用$remote_addr+$request_time做兜底(注意:$request_time不能用,它在日志阶段才可用;应改用$msec或固定字符串如"fallback")
正确构造哈希输入:避免空值与动态变量污染
常见错误是直接写 split_clients "$cookie_userid" $upstream_group { ... },结果大量请求因 Cookie 缺失被分进同一组(空字符串 MD5 是确定的)。真正可控的做法是预处理出一个“稳定键”:
map $http_x_user_id $canary_key {
"" $remote_addr;
default $http_x_user_id;
}
map $cookie_userid $canary_key_fallback {
"" $canary_key;
default $cookie_userid;
}
# 最终键:支持 Header > Cookie > IP
map $canary_key_fallback $routing_key {
"" $binary_remote_addr;
default $canary_key_fallback;
}
这样 $routing_key 永远非空,且对同一 UserID 总生成相同哈希值。注意:$binary_remote_addr 比 $remote_addr 更适合哈希(IPv4 固定 4 字节,IPv6 固定 16 字节),避免字符串长度干扰分布。
split_clients 分组比例写法易错:范围必须连续且覆盖 100%
split_clients 内部按 0–99 整数哈希取模,然后匹配百分比区间。错误示范:10% .v1; 20% .v2; —— 这实际只覆盖了 0–29,剩下 70% 流量全落到默认组(即没匹配到的最后一个声明组)。必须显式补满:
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
split_clients "${routing_key}" $upstream {
0-9 ".v1";
10-29 ".v2";
30-99 ".v3";
}
- 区间用连字符
-,不是波浪线~或省略号 - 数字是整数,不支持小数(如
5.5%语法错误) - 最后一组务必覆盖剩余全部(如
30-99),否则 nginx 启动失败或行为未定义
与 upstream 动态拼接时,proxy_pass 不支持变量嵌套解析
很多人写 proxy_pass http://backend$upstream; 期望根据 $upstream 值选择不同 upstream 块,但这是无效的:nginx 在配置加载阶段就要求 proxy_pass 的地址必须能静态解析。正确做法只有两个:
- 用
upstream+server配合weight和down做静态分组,再用split_clients控制流量打到哪个逻辑分组(需配合ip_hash或自定义 hash 键保证会话粘性) - 更灵活的是用
lua-resty-upstream或 OpenResty 的balancer_by_lua_block,在运行时根据$upstream变量 select server
纯 nginx 官方模块下,split_clients 只能用于生成变量,不能直接驱动路由跳转——它本质是个“条件赋值器”,不是“动态代理控制器”。
实际部署时最容易被忽略的是 worker 进程间哈希一致性:确保所有 worker 使用相同编译参数和 nginx 版本,因为 MurmurHash 实现可能随版本微调;另外,$routing_key 若含时间类变量(如误用 $time_iso8601),会导致同一请求在重试时被分到不同组。










