nginx 不支持 upstream 块嵌套,必须定义在 http{} 顶层;分层调度需通过 map、split_clients 或多级 proxy_pass + rewrite 等运行时机制实现,而非配置结构嵌套。

Nginx 不支持 upstream 块的直接嵌套,比如在另一个 upstream 里写 upstream 或用 upstream 包裹 upstream。这种写法会触发配置错误:unknown directive "upstream",因为 upstream 只能定义在 http{} 块顶层,不能出现在 server{}、location{} 或其他 upstream{} 内部。
真正可行的做法,是用逻辑分层替代结构嵌套——把“哪类请求发给哪组后端”这个决策,从配置层级转移到请求处理流程中。
✅ 正确实现分层调度的常用方式
使用 map 指令动态选择 upstream 名称
适用于按域名、路径、Header、IP 等特征分流到不同后端组。
http {
# 定义多个 upstream 组(必须在 http 块顶层)
upstream api_v1 {
server 10.0.1.10:3000;
server 10.0.1.11:3000;
}
upstream api_v2 {
server 10.0.2.20:3000 weight=2;
server 10.0.2.21:3000;
}
upstream admin_backend {
server 10.0.3.30:8080;
}
# 根据 host 或 path 映射到 upstream 名称(字符串变量)
map $host $backend_group {
default "api_v1";
~^v2\.api\.example\.com$ "api_v2";
~^admin\.example\.com$ "admin_backend";
}
server {
listen 80;
server_name ~^(?<sub>.+)\.example\.com$;
location / {
proxy_pass http://$backend_group; # 动态引用
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}</sub>
⚠️ 注意:
proxy_pass后必须带协议(http://)和变量名,不能写成http://$backend_group/加尾部斜杠(会导致路径重复拼接)。
利用 split_clients 实现灰度或流量比例分层
适合 A/B 测试、新老版本按百分比分流。
http {
split_clients "${remote_addr}AAA" $upstream_name {
50% "backend_v1";
50% "backend_v2";
}
upstream backend_v1 {
server 192.168.1.100:8080;
}
upstream backend_v2 {
server 192.168.1.101:8080;
}
server {
location / {
proxy_pass http://$upstream_name;
}
}
}
✅
split_clients基于哈希保证同一客户端始终命中同一组,适合需稳定路由的场景。
多级 proxy_pass + rewrite 实现路径导向分层
适合按 URI 前缀划分服务域(如 /user/ → 用户服务,/order/ → 订单服务)。
http {
upstream user_svc {
server 10.10.1.1:8001;
server 10.10.1.2:8001;
}
upstream order_svc {
server 10.10.2.1:8002;
server 10.10.2.2:8002;
}
server {
location ^~ /user/ {
rewrite ^/user/(.*)$ /$1 break;
proxy_pass http://user_svc;
}
location ^~ /order/ {
rewrite ^/order/(.*)$ /$1 break;
proxy_pass http://order_svc;
}
# 兜底默认入口
location / {
proxy_pass http://user_svc;
}
}
}
?
^~优先匹配前缀,避免正则干扰;rewrite ... break确保路径正确透传。
❌ 常见错误写法(务必避免)
- 把
upstream写进server{}或location{}块内 - 在
upstream块里再写upstream xxx { ... } -
proxy_pass直接写变量但没加http://协议头(如proxy_pass $backend;→ 错误) -
map中值未加引号导致空格解析失败(应写"group_a",而非group_a)
不复杂但容易忽略。核心就一条:upstream 是静态定义的资源,分层靠运行时变量驱动,而不是靠配置块嵌套。











