least_conn仅统计tcp活跃连接数,含空闲keepalive连接和僵尸连接,不反映真实负载;需检查upstream配置、启用健康检查、对比ss与nginx_status验证连接状态,并可临时切换算法定位问题。

排查 least_conn 路由错乱,核心是确认 Nginx 实际统计的“连接数”是否真实反映后端负载。它不统计请求处理时长、不感知应用层忙闲,只看当前活跃 TCP 连接数(keepalive 期间也计入),误差往往就出在这里。
确认 least_conn 统计的是什么连接
Nginx 的 least_conn 算法仅依据每个 upstream server 当前建立且未关闭的 TCP 连接数做选择,包括:
- 正在传输请求/响应的连接
- 启用了
keepalive且处于空闲等待状态的长连接(只要没超时或被主动关闭,就算在内) - 后端已断开但 Nginx 尚未检测到(如未启用
tcp_check或keepalive_timeout设置过长)的“僵尸连接”
它完全不关心:CPU 使用率、内存占用、请求排队数、响应延迟、后端线程是否阻塞。如果后端用短连接+高并发,或长连接复用率极高,这个数字就极易失真。
检查 upstream 配置是否放大统计偏差
以下配置会显著干扰 least_conn 的合理性:
-
keepalive 32;+ 极长的proxy_http_version 1.1;+ 未配proxy_set_header Connection '';→ 导致大量空闲连接长期滞留,某台机器哪怕已无请求处理,连接数仍居高不下 -
proxy_next_upstream error timeout http_502;缺失或过于宽松 → 连接异常中断(如后端 OOM kill 进程)后,Nginx 无法及时摘除节点,继续往故障机发新连接,连接数虚低 - 未启用
health_check(如基于match的主动健康检查)→ 无法发现后端进程僵死、TCP 可通但 HTTP 不响应的情况,此时连接数可能很低,但实际不可用
实时验证各 upstream server 的真实连接数
别只信日志或监控图表,直接查 Nginx 内部状态:
- 启用
stub_status模块并配置 location,访问/nginx_status可看到总连接数,但不细分 upstream;更有效的是使用ngx_http_upstream_module的变量:$upstream_addr和$upstream_connect_time配合 access_log,可记录每次选中的后端及建连耗时 - 开启详细 upstream 日志:
log_format upstreamlog '[$time_local] $remote_addr - $upstream_addr ~ $upstream_connect_time ~ $upstream_header_time ~ $upstream_response_time';
观察是否频繁打到同一台(连接数显示少但实际慢)、或某台连接数突降却无错误日志(可能连接被悄悄重置) - 用
ss -tnp | grep :<port></port>在 Nginx 本机抓取 ESTABLISHED 连接,比对curl http://127.0.0.1/nginx_status中 Active connections 数量,再结合lsof -i :<upstream_port></upstream_port>看具体连向哪台后端 —— 这是验证“统计值”和“真实 TCP 状态”是否一致的黄金方法
临时绕过 least_conn 快速定位是否为算法问题
不是所有错乱都源于 least_conn,先排除其他因素:
- 把 upstream 的
least_conn改成ip_hash或round_robin(默认),观察路由是否稳定 → 如果错乱消失,基本锁定是least_conn统计逻辑与业务模型不匹配 - 在 upstream 块中为每台 server 显式添加
max_conns=1024;(需 Nginx ≥ 1.11.5),配合queue 30s;,可防止单台过载被持续打爆,同时让 Nginx 主动排队而非盲目转发 - 若后端是 Java 应用,检查是否开启了 HTTP/2 或使用了 Netty 等框架的连接池,它们常维持大量空闲连接 —— 此时
least_conn失效是设计使然,应改用hash $request_uri consistent;或接入外部指标(如 Prometheus + nginx-module-vts + 自定义 LB 插件)











