least_conn长连接未释放问题核心是三方节奏错位,需重点检查proxy_read_timeout是否小于后端空闲周期、upstream keepalive及proxy_http_version/connection头是否正确配置、后端响应是否含connection: close、stub_status与ss统计是否一致,以及健康检查能否识别假存活节点。

排查 least_conn 策略下的长连接未释放问题,核心是定位“连接该断不断”的具体环节——不是 Nginx 不想放,而是客户端、Nginx 代理、后端服务三者在连接生命周期管理上节奏错位。重点查超时配置是否匹配、keepalive 是否被误干扰、后端是否静默挂起。
检查 proxy_read_timeout 是否远小于后端实际空闲周期
这是最常见原因。least_conn 依赖长连接复用,但若 proxy_read_timeout 设得太短(比如默认 60 秒),而你的长轮询后端平均等待 8 秒才返回,或 WebSocket 心跳间隔为 30 秒,Nginx 就会在后端还没响应前就主动断连,导致连接反复重建、连接数虚高、least_conn 统计失真。
- proxy_read_timeout 应设为后端最长空闲等待时间的 1.5–2 倍:长轮询设 15–30 秒,WebSocket 设 60–90 秒(心跳间隔 × 2),纯后台推送可设 300 秒以上
- 务必确认该指令作用于 location 块内,而非仅写在 server 或 http 全局——作用域错误会导致不生效
- 用 curl -v 访问接口,观察响应头中是否含 Connection: close;若有,说明某处配置强制关闭了连接
验证 upstream keepalive 是否真正启用且未被覆盖
least_conn 的调度效果严重依赖连接复用。如果 upstream 没配 keepalive,或 location 中 proxy_http_version / Connection 头配置缺失,Nginx 实际走的是短连接,此时“活跃连接数”始终趋近于 0,least_conn 退化为随机分发。
- upstream 块必须显式包含 keepalive N(建议 16–64,按后端并发能力调整)
- 对应 location 中必须有 proxy_http_version 1.1 和 proxy_set_header Connection ''(注意是空字符串,不是 "keep-alive")
- 检查后端响应头是否含 Connection: close —— 常见于 Spring Boot 开启 debug 模式、Nginx 后面套了 WAF、或后端代码显式设置了 response.setHeader("Connection", "close")
比对后端真实 ESTABLISHED 连接与 Nginx 统计是否一致
Nginx 的 least_conn 决策依据是它自己维护的连接计数,但如果后端提前断连、或存在 TIME_WAIT 残留、或连接被防火墙静默回收,Nginx 可能还“以为”连接活着,导致新请求继续打向已失效的连接槽位。
- 在后端机器执行 ss -tn state established '( dport = :8080 )' | wc -l,实时统计真实连接数
- 同时访问 Nginx 的 /nginx_status(需启用 stub_status),看 Active connections 和各 upstream server 的 conn_active 值
- 若后端 ss 统计为 50,而 stub_status 显示某 server conn_active 为 120,说明 Nginx 计数滞后——大概率是后端未正确发送 FIN 包,或网络中间件拦截了 RST/ACK
确认健康检查是否屏蔽了“假存活”节点
least_conn 不判断后端是否真能处理请求,只看连接数。一个后端可能 TCP 连接正常(ESTABLISHED),但线程卡死、GC 暂停、数据库锁表,此时连接数低却无法响应,least_conn 反而会持续导流过去。
- 启用主动健康检查(如 nginx-plus 或带 health_check 补丁的开源版),探测路径独立于业务,返回码 + 关键 header(如 X-Status: ok)双校验
- 若只能用被动检查,确保 proxy_next_upstream 包含 timeout error http_500 http_502,并配 max_fails=2 fail_timeout=15s
- 观察 access log 中失败请求是否集中出现在某台后端——若是,说明 least_conn 在“选最少连接”,但没识别出它已不可用











