反向代理是 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 超时 | 检查后端慢查询/日志 |
| 404 | proxy_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 核心配置结构 — server/location 上下文与变量基础
- Nginx HTTPS 与 TLS 加固 — 反向代理的 TLS 加密层
- Nginx 缓存与压缩优化 — 上游响应缓存与 gzip
- Nginx 限流与安全防护 — 网关层的访问控制
- Nginx 日志分析与性能调优 — 代理链路的性能指标
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。