Nginx被动健康检查需max_fails、fail_timeout与proxy_next_upstream三者协同生效:在fail_timeout窗口内累计失败max_fails次即标记节点不可用并跳过该节点fail_timeout秒,到期后以首个请求试探恢复;必须显式配置proxy_next_upstream指定错误类型,且超时参数需对齐后端响应特征。

轮询策略本身不检测后端是否可用,失效节点会持续接收请求,直到你配置并验证健康检查机制。排查核心是确认“Nginx 是否真识别到了故障”,而不是只看请求分发是否均匀。
检查被动健康检查是否真正启用
这是最常被遗漏的一环:max_fails 和 fail_timeout 必须与 proxy_next_upstream 配合使用,三者缺一不可。
- 在 upstream 中每个 server 后明确写上 max_fails=3 fail_timeout=30s(不能只写在某一个上)
- 在 location 块中必须有 proxy_next_upstream error timeout http_500 http_502 http_503 http_504;注意不能用 http_50x,也不能漏掉 timeout
- 必须设置超时参数:proxy_connect_timeout 5s; proxy_send_timeout 10s; proxy_read_timeout 10s;否则请求卡住,失败无法及时触发
- 查看 error log,搜索 upstream failed 或 no live upstreams,确认是否出现重试日志和节点标记 down 的记录
验证后端实际响应是否符合重试条件
Nginx 只对 proxy_next_upstream 明确列出的状态码或错误类型才重试。很多问题出在“后端返回了 500 但没被重试”,原因往往是:
- 后端返回的是 500 以外的非标准状态码(如 599、000),需手动加入列表
- 后端返回 200 但 body 包含错误(如 {“code”:500}),Nginx 不感知业务逻辑,不会重试
- 超时未生效:proxy_read_timeout 太大,导致请求挂起几十秒才失败,掩盖了节点真实不可用
- 用 curl -v --connect-timeout 3 http://backend-ip:port 模拟单次请求,观察是否真能连通、是否真返回预期状态码
确认节点是否已被标记为 down 并试探恢复
Nginx 不定时轮询恢复,而是“按需试探”:当一个节点被标记为 down 后,下一次本该轮到它时,Nginx 会单独发一次请求试探。
- 试探成功(连得上 + 返回正常状态码 + 未超时)→ 立即恢复参与轮询
- 试探失败 → 继续保持 down,等待下次轮到再试
- 若所有节点都 down,Nginx 会强制遍历全部节点重试,此时刚恢复的服务可立即承接流量
- 可通过 $upstream_addr 日志变量确认实际转发目标,比单纯看轮询顺序更可靠
排除连接层干扰因素
有些“失效”其实是连接复用或系统资源导致的假象:
- 检查是否启用了 keepalive:如果 upstream 块里配了 keepalive 32,但后端主动断连,可能导致连接池残留脏连接,建议搭配 proxy_http_version 1.1 和 proxy_set_header Connection '' 使用
- 确认后端进程是否真退出:用 netstat -tuln | grep :8080 或 ss -tuln | grep :8080 查端口监听状态
- 检查系统级限制:worker_rlimit_nofile 是否过低?ulimit -n 查看当前限制,避免连接数耗尽后新请求直接被拒绝
- 留意 time_wait 连接堆积:高并发短连接场景下,大量 TIME_WAIT 可能占满本地端口,影响建连成功率


















