关键不是看请求分发次数,而是看连接实际落在哪、留了多久、是否被重分配;需统计$upstream_addr唯一IP:PORT对、用ss查ESTABLISHED连接数,并改用least_conn算法、收紧keepalive参数(如keepalive 16–32、keepalive_requests 20–50)、禁用ip_hash等强制绑定策略。

排查 KeepAlive 连接复用引发的后端负载倾斜,关键不是看“请求分发了多少次”,而是看“连接实际落在哪、留了多久、有没有被重分配”。轮询算法对长连接天然失敏,问题往往藏在连接分布和生命周期里。
确认是否真为连接级失衡
日志里请求数平均 ≠ 连接分布均匀。一个复用连接可能承载上百个请求,却只记作一次 upstream_addr。要验证是否倾斜,得统计连接粒度:
- 查 Nginx access log 中 $upstream_addr 字段,提取 唯一 IP:PORT 对(不是每行都算,要去重),观察是否长期固定在 1–2 台后端
- 在后端服务器执行 ss -tn state established | grep :端口 | wc -l,对比各节点 ESTABLISHED 连接数;若某台是其他节点的 3 倍以上,基本可定性为连接堆积
- 临时关闭 keepalive:注释掉 upstream 中的 keepalive N 并重启,观察连接分布是否立刻变均匀——若明显改善,说明原配置放大了复用绑定效应
检查调度算法是否适配长连接
轮询(包括加权轮询)只控制新请求派发节奏,不感知当前连接数。它无法阻止一个长连接持续打到同一台后端。
- 必须改用 least_conn:在 upstream 块中显式声明,让 Nginx 每次选连接数最少的节点,而非按顺序轮着来
- 禁用 ip_hash 或 hash $request_uri:它们强制绑定,会彻底锁死连接去向,与复用场景互斥
- 避免混用 max_conns 和 least_conn 失配:比如某台后端 max_conns=100,但实际活跃连接已到 95,least_conn 仍可能选它——需配合健康检查或主动剔除逻辑
收紧连接复用参数,促连接再分配
keepalive 不是开得越大越好。池子太大、连接驻留太久,等于把流量“焊死”在少数节点上。
- 设置 keepalive 16–32(非默认值):每个 worker 缓存空闲连接数,建议按峰值并发的 1/4~1/2 设
- 限制单连接请求数:keepalive_requests 20–50,避免一个连接服务几百次请求;配合 keepalive_timeout 30s,双条件任一满足即回收
- location 中必须加:proxy_http_version 1.1; 和 proxy_set_header Connection '';,否则后端返回的 Connection: close 会让 Nginx 主动断开复用
交叉验证连接状态与日志行为
单看 access log 或 stub_status 都不够,需三处对齐:
- 在 log_format 中加入 $connection_requests:若某客户端的该值从 1 持续涨到 80+,说明连接未释放、请求全扎堆在一台后端
- error_log 开 debug 级别,搜 "upstream connection is busy":意味着 keepalive 连接池已空,Nginx 被迫新建连接,least_conn 失效
- 对比 $upstream_connect_time 和 $upstream_response_time:若某台 connect_time 正常但 response_time 持续偏高,说明连接虽在,但后端处理慢或线程阻塞,需查其应用层而非 Nginx 配置


















