Nginx 反向代理与负载均衡:upstream 与 proxy_pass 实战

深入讲解反向代理与负载均衡。涵盖 proxy_pass 转发、upstream 加权与哈希、健康检查、超时重试、WebSocket 长连接与灰度发布。

反向代理是 Nginx 最核心的生产能力:把客户端请求转发给内部应用服务器,同时对外隐藏真实架构。负载均衡则解决"转发给谁"的问题——当有多台后端时,按策略分配流量并自动摘除故障节点。本章衔接《Nginx 核心配置结构》的上下文知识,重点讲解 proxy_pass 的 URL 拼接语义、upstream 块的各类负载均衡算法、健康检查机制,以及生产环境必须掌握的代理超时、重试与长连接调优。

核心认知:proxy_pass 决定"转发的目标和路径如何拼接",upstream 决定"在多个目标之间如何选路"。二者结合,才能构建高可用的网关层。

1. proxy_pass 转发语义

1.1 带 URI 与不带 URI 的区别

proxy_pass 是否携带 URI,决定路径如何拼接到后端:

# 不带 URI:保留完整原始 URI 转发
location /api/ {
    proxy_pass http://backend;               # 请求 /api/user → http://backend/api/user
}

# 带 URI:替换匹配部分
location /api/ {
    proxy_pass http://backend/;              # 请求 /api/user → http://backend/user
}

location /api/ {
    proxy_pass http://backend/v2;            # 请求 /api/user → http://backend/v2/user
}

1.2 常见转发配置模板

location / {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}
请求头目的
Host保留原域名,后端做虚拟主机识别
X-Real-IP透传客户端真实 IP
X-Forwarded-For追加转发链中的 IP 列表
X-Forwarded-Proto标记原始协议 http/https

避坑:proxy_set_header 写在 location 内时,若父级也定义了同名字段,location 内必须完整重写,否则父级值会覆盖。建议用 include proxy_params; 统一管理。

2. upstream 与负载均衡策略

2.1 upstream 块基础

upstream backend {
    server 10.0.0.11:8080;
    server 10.0.0.12:8080;
    server 10.0.0.13:8080;
}

2.2 常见负载均衡算法

算法指令特点适用场景
轮询默认依次分发后端无状态、性能均匀
加权轮询weight按权重分配比例异构机器、容量不同
最少连接least_conn分给活跃连接最少的后端长请求、耗时不均匀
IP 哈希ip_hash同一 IP 固定同一后端需会话保持
一致性哈希hash $request_uri按 key 哈希、增删节点影响小缓存类后端
upstream backend {
    least_conn;                              # 最少连接
    server 10.0.0.11:8080 weight=3;          # 权重 3
    server 10.0.0.12:8080 weight=1;          # 权重 1
    server 10.0.0.13:8080 down;              # 手动摘除
}

2.3 ip_hash 与一致性哈希

upstream backend {
    ip_hash;                                 # 同一客户端 IP 会话保持
    server 10.0.0.11:8080;
    server 10.0.0.12:8080;
}

upstream cache_backend {
    hash $request_uri consistent;            # 一致性哈希,节点增减影响最小
    server 10.0.0.21:9001;
    server 10.0.0.22:9001;
    server 10.0.0.23:9001;
}

避坑:ip_hash 在客户端经过多层代理后会拿到代理 IP,需要配合 $remote_addr 的真实 IP 解析才有效;hash consistent 才能应对节点扩缩容,普通 hash 在节点变化时会大量重排。

3. 健康检查机制

3.1 被动健康检查

Nginx 默认采用被动检查:请求失败达到阈值即标记失败,配合 max_fails 与 fail_timeout:

upstream backend {
    server 10.0.0.11:8080 max_fails=3 fail_timeout=30s;
    server 10.0.0.12:8080 max_fails=3 fail_timeout=30s;
}
参数含义推荐值
max_fails时间内允许的失败次数3
fail_timeout统计窗口与暂停时长(秒)30
backup备用节点,主节点全挂才启用—
down手动标记不可用—

3.2 主动健康检查与备用节点

Nginx 开源版的主动检查依赖第三方模块,也可用 backup 实现主备切换:

upstream backend {
    server 10.0.0.11:8080;
    server 10.0.0.12:8080;
    server 10.0.0.99:8080 backup;           # 备用节点
}

3.3 检查脚本实现主动探测

开源场景常用外部探活 + 动态更新配合:

# 每 5 秒探测后端,失败则用 upstream_conf 动态下掉
curl -fsS http://10.0.0.11:8080/health || \
  echo "server 10.0.0.11:8080 down;" | nginx -s reload

核心认知:被动健康检查只对"有请求经过"的节点生效,冷节点故障无法及时被发现。要求严格的场景应引入主动探活或商业版 health_check。

4. 代理超时与重试

4.1 三级超时

location / {
    proxy_connect_timeout   5s;      # 建立上游连接超时
    proxy_send_timeout      10s;     # 发送请求体超时
    proxy_read_timeout      30s;     # 读取上游响应超时
}

4.2 失败重试策略

location / {
    proxy_next_upstream error timeout invalid_header http_502 http_503;
    proxy_next_upstream_tries 3;          # 最多尝试 3 个后端
    proxy_next_upstream_timeout 5s;       # 总重试窗口
}
值触发重试的失败类型
error连接/读写出错
timeout超时
http_502 http_503网关/服务不可用
http_500后端内部错误(不建议)
non_idempotent非幂等请求也可重试(有风险)

避坑:POST 等非幂等请求被 proxy_next_upstream 重试可能造成重复下单,除非明确知道后端幂等,否则不要轻易对非幂等请求开启重试。

5. WebSocket 与长连接

5.1 WebSocket 协议升级

location /ws/ {
    proxy_pass http://ws_backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 3600s;          # 长连接读超时拉长
}

5.2 上游 keepalive 连接复用

upstream backend {
    server 10.0.0.11:8080;
    keepalive 32;                      # 与上游保持 32 个空闲连接
}

location / {
    proxy_pass http://backend;
    proxy_http_version 1.1;            # 复用连接必须 HTTP/1.1
    proxy_set_header Connection "";    # 清空默认 Connection 头
}
参数作用推荐值
keepalive 32每 worker 的空闲上游连接数16~64
proxy_http_version 1.1开启持久连接的前提1.1
proxy_set_header Connection ""避免连接头干扰复用空

核心认知:keepalive 在 upstream 块里定义的是"空闲连接池大小",配合 HTTP/1.1 可大幅降低与后端之间频繁建连的握手开销。

6. resolver 与动态上游

6.1 变量型 proxy_pass 的 DNS 解析

location / {
    resolver 10.0.0.53 valid=30s;      # 指定 DNS 与缓存时间
    proxy_pass http://backend.service.consul:8080;
}

当 proxy_pass 使用变量时,Nginx 会在每个请求实时解析域名,必须配置 resolver,否则报 no resolver defined。

6.2 动态上游的实现方式

upstream backend {
    server 10.0.0.11:8080;
    server 10.0.0.12:8080;
}

# 通过 njs 或 Lua 在请求期按 key 选择后端
location /canary/ {
    set $backend http://10.0.0.11:8080;
    if ($cookie_canary = "new") {
        set $backend http://10.0.0.12:8080;
    }
    proxy_pass $backend;               # 变量型 proxy_pass 支持灰度分流
}

避坑:变量型 proxy_pass 会让每个请求触发 DNS 查询,务必配置 resolver 并合理设置 valid 缓存,避免查询风暴。

7. 生产实践与排错

7.1 统一代理参数片段

# conf.d/proxy_params.conf
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 5s;
proxy_read_timeout 60s;
proxy_http_version 1.1;
server {
    location /api/ {
        include proxy_params;
        proxy_pass http://backend;
    }
}

7.2 常见排错

现象可能原因排查命令
502 Bad Gateway后端未启动或端口错curl -v http://backend:8080/health
504 Gateway Timeout后端响应过慢,read 超时检查后端慢查询/日志
404proxy_pass URI 拼接错核对是否带 URI、alias/root
上游连接数打满keepalive 未开或过小观察后端连接数与 ss -s
循环重定向转发后 Host 变化确认 X-Forwarded-Proto 与后端配置

7.3 灰度发布示例

upstream backend {
    server 10.0.0.11:8080;                     # 稳定版
    server 10.0.0.12:8080 weight=9;            # 稳定版主流量
    server 10.0.0.13:8080 weight=1;            # 新版灰度 10%
}

8. 总结

环节要点
proxy_pass带 URI 会替换匹配前缀,不带则保留原始路径
请求头Host/X-Real-IP/X-Forwarded-For 统一透传
负载均衡轮询、weight、least_conn、ip_hash、hash consistent
健康检查max_fails/fail_timeout 被动摘除,backup 主备切换
超时重试三级超时 + proxy_next_upstream,注意非幂等风险
长连接upstream keepalive + HTTP/1.1 复用
动态解析变量型 proxy_pass 必须配 resolver
生产实践统一 proxy_params,灰度用 weight 分流

反向代理把网关与后端应用解耦,负载均衡让集群扩容与故障自愈成为可能。配置好 upstream 后,下一步需要为这些请求加上 HTTPS 加密与 TLS 加固,让流量在公网传输时安全可信。

延伸阅读

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「nginx」更多文章

  1. Nginx Ingress Controller:Kubernetes 云原生网关的路由、证书与金丝雀发布
  2. Nginx 监控与可观测性:stub_status 指标、Prometheus 集成与容量规划
  3. Nginx 静态资源与页面加速:零拷贝、缓存验证与 Brotli 压缩联动