如果你正在处理Nginx,先别急着照搬网上的参数。后端服务器偶尔会挂——数据库连接池耗尽、进程 OOM、网络瞬间抖动,这些情况在你的生产环境里迟早出现。问题的关键不在“会不会挂”,而在于 Nginx 在服务器挂了之后会干什么。
默认配置下,Nginx 会耐心等待 proxy_connect_timeout 规定的秒数,如果连不上,再返回 504。一个 5 秒的超时,乘以 10 个请求,用户就要干等半分钟。这是最差的体验。真相是:Nginx 可以快速发现故障并立刻把请求转给其他后端,不需要让用户看到 504 或等待。
做到这件事只需要两个指令:proxy_connect_timeout 和 proxy_next_upstream。下面从概念讲到调优,覆盖新手最容易搞混的地方。
代理超时:连接、读取、发送分别控制什么?
验证与回滚
Nginx 作为反向代理时,与上游服务器(后端)的通信分三个阶段:
- 连接阶段:Nginx 尝试与上游 IP:Port 建立 TCP 连接。控制指令是
proxy_connect_timeout,默认 60 秒。 - 发送请求阶段:连接建立后,Nginx 把从客户端收到的请求数据转发给上游。超时用
proxy_send_timeout,默认也是 60 秒。 - 接收响应阶段:上游开始返回数据,但如果两次读操作间隔过长,会触发
proxy_read_timeout,默认 60 秒。
故障转移关心的是第一阶段——连接超时。如果上游 Linux 内核已经半连接或端口被防火墙阻断,握手会卡住很久。把 proxy_connect_timeout 从 60 秒降到 3~5 秒,可以让 Nginx 快速失败,而不是吊死在一棵树上。
proxy_next_upstream:故障转移的核心开关
故障定位思路
这个指令告诉 Nginx:“当本次与上游通信出现某种问题时,要不要换下一个上游节点重试?”它接受多个参数,常用如下:
error—— 与上游建立连接、发送请求或读取响应时发生错误(如连接被拒绝、超时、未收到完整响应等)timeout—— 在proxy_connect/read/send_timeout规定时间内未完成操作invalid_header—— 上游返回的响应头格式错误或为空http_500/http_502/http_503/http_504—— 上游返回了指定的 HTTP 状态码non_idempotent—— 默认情况下,Nginx 不会重试 POST、PATCH、DELETE 等非幂等请求,加上这个参数才会重试(但风险很大,慎用)
默认行为是:proxy_next_upstream error timeout。也就是说,Nginx 只会在连接出错或超时时转移请求,而不会因为上游返回 500 就重试。很多新手以为 Nginx 会自动跳过返回 500 的节点,其实需要主动加 http_500。
Nginx:把超时时间和重试策略搭配起来
容易忽略的细节
只看单条指令是不够的,关键是组合后的效果。
假设 proxy_connect_timeout 5s,但没有开启 proxy_next_upstream timeout(或者只开了 error),那么上游连不上时 Nginx 会在 5 秒后报错,但不会换下一个节点,最终返回 504 给用户。
反过来,如果只配了 proxy_next_upstream error timeout,但 proxy_connect_timeout 还是 60 秒,用户要等一分钟才触发转移,等于没优化。
推荐配置组合(入门级):
upstream backend {
server 10.0.0.1:8080 max_fails=2 fail_timeout=10s;
server 10.0.0.2:8080 max_fails=2 fail_timeout=10s;
}
server {
location / {
proxy_pass http://backend;
proxy_connect_timeout 3s;
proxy_send_timeout 10s;
proxy_read_timeout 30s;
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;
proxy_next_upstream_tries 2;
}
}
解释每一条:
proxy_connect_timeout 3s:连接超时设为 3 秒,比默认快 20 倍。proxy_next_upstream ... error timeout invalid_header http_500 http_502 http_503:除了错误和超时,如果上游返回 5xx,也立即尝试下一个节点。proxy_next_upstream_tries 2:最多尝试 2 个节点(包含第一次)。如果设成 3,Nginx 会尝试所有可用节点。
注意 upstream 块里的 max_fails 和 fail_timeout 是 Nginx 自带的被动健康检查机制。它和 proxy_next_upstream 是两层逻辑:proxy_next_upstream 决定是否在当前请求中换节点;max_fails 决定一段时间内累计失败多少次后把节点标记为 down,后续请求不再发给它。它们协同工作,前者保证单次请求的快速转移,后者保证长期持续的故障不会不断重试。
相关阅读:此处可内链到“Nginx常见问题”专题。
常见误区与风险
非幂等请求不要重试
默认 Nginx 不会重试 POST 等非幂等请求。如果你强行加 non_idempotent,可能会重复扣款、重复创建订单。如果你确实需要重试(比如接口本身就是幂等的),才可以用,但一定要确认。
http_504 要不要加?
如果上游本身正常但处理超时(例如请求一个慢 SQL),504 是来自 Nginx 自身(上游超时),而不是上游返回的。这种情况下 Nginx 不会重试,因为 Nginx 认为错误是自己产生的。所以 http_504 加了也没用,因为上游根本没返回 504。正确的做法是用 timeout 参数覆盖 read 超时的情况。
proxy_next_upstream_timeout 与 proxy_next_upstream_tries
Nginx 1.11.0 之后还有两个相关参数:proxy_next_upstream_timeout 和 proxy_next_upstream_tries。前者限制整个重试链的总耗时,后者限制总尝试次数。建议总时间不超过客户端超时的一半,例如客户端超时 30 秒,则 proxy_next_upstream_timeout 设为 15 秒。
验证配置是否生效
我的处理经验
改完配置文件后,reload 生效:nginx -t && systemctl reload nginx。
然后模拟故障:
- 临时停掉一台后端:
systemctl stop your-app。 - 用
curl -w "%{http_code} %{time_total}n" http://your-domain观察返回状态码和总耗时。 - 如果配置正确,你会看到请求在几十毫秒内成功返回(因为 Nginx 立即连接下一个后端),而不是等几秒后返回 504。
更进一步的验证可以用 ab 或 wrk 做压力测试,同时观察 Nginx 访问日志中上游地址是否在故障时发生了切换。
想继续深入:此处可内链到“Nginx优化清单”文章。
补充参考:此处可内链到“Nginx故障排查实例”。
延伸阅读:此处可内链到“Nginx配置案例”相关文章。
调优建议:先降超时,再加重试,再调状态码与Nginx
容易忽略的细节
如果你的业务可以接受较短的连接等待时间(通常局域网内后端 1-3 秒足够),可以按这个步骤逐步收紧:
- 将
proxy_connect_timeout从默认 60s 降至 5s; - 确认
proxy_next_upstream包含error timeout; - 观察一周,如果没有误判(比如网络抖动造成的不必要转移),再降至 3s;
- 如果后端业务偶发 500,可以加入
http_500 http_502 http_503; - 如果后端是长耗时 API,考虑
proxy_next_upstream_timeout限制总重试时间。
不要急于一步到位。过低的超时可能让正常的慢节点被反复跳过,导致所有流量集中到正常节点上,反而降低整体可用性。好在 Nginx 的重试策略是“有记忆的”(配合 max_fails),极端场景下即使配置激进,系统也会自动恢复。
最后,记得在 upstream 块中正确设置所有后端节点的权重和备份标志。配合 proxy_next_upstream,你的 Nginx 才能真正做到“后端挂了也不怕”。
延伸阅读
