先确认真实连接状态:用ss -tn state established '( dport = :8080 )' | wc -l查后端established数,若远超理论最大空闲数(如keepalive×worker_processes),说明连接未回收;再检查upstream keepalive配置、http版本、connection头及两端超时对齐。

核心是验证连接是否真在复用、有没有堆积、是否被后端提前关闭——不能只看配置,要拿运行时数据说话。
确认连接是否实际堆积
先别改配置,用系统命令看真实连接状态:
- 查当前到后端的真实 ESTABLISHED 连接数:ss -tn state established '( dport = :8080 )' | wc -l(把 8080 换成你的后端端口)
- 算理论最大空闲连接数:如果 upstream 配了 keepalive 32、Nginx 有 worker_processes 4,那最多应有 32 × 4 = 128 个空闲长连接;若实际查出 500+,说明连接没回收
- 对比同一后端各实例的连接分布:ss -tn state established | grep :8080 | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -nr,看是否集中在某几台机器上
检查 keepalive 是否真正生效
以下任一缺失都会让 upstream keepalive 形同虚设:
- upstream 块里没写 keepalive N(例如 keepalive 128),默认完全不启用长连接
- location 中没加 proxy_http_version 1.1(HTTP/1.0 默认关闭连接)
- 没写 proxy_set_header Connection ""(若客户端请求带 Connection: close,Nginx 会原样透传,导致后端主动断连)
- log_format 中没包含 $upstream_addr 和 $upstream_response_time,无法定位慢节点或复用异常
验证两端超时是否对齐
Nginx 不会感知后端是否已关连,必须靠超时值协同:
- 查后端空闲超时:Tomcat 看 connectionTimeout,Spring Boot 看 server.tomcat.connection-timeout,Node.js 看 server.keepAliveTimeout
- Nginx 的 keepalive_timeout 必须 ≤ 后端值 − 10s(例如后端设 60s,Nginx 就设 50s)
- 避免设为 0 或过大(如 120s):否则失效连接滞留池中,可用连接数持续下降
- Kubernetes 环境下,Pod 退出前需 preStop Hook + terminationGracePeriodSeconds ≥ keepalive_timeout + 几秒缓冲,否则 Nginx 还在复用,Pod 已销毁
抓日志和指标交叉定位泄漏点
仅看配置不够,要抓运行时证据:
- 开启 debug 日志(需编译含 --with-debug):error_log /var/log/nginx/error.log debug;,重点搜 keepalive free、keepalive close、dummy_handler 等关键词,确认空闲连接是否真被放回池中
- 观察 error_log 中是否高频出现 upstream connection is busy 或 no live upstreams,这往往意味着连接池被无效连接占满
- 配合后端监控:登录每台应用服务器,执行 netstat -anp | grep :8080 | wc -l,再结合 ss -tan state time-wait | wc -l 判断是否大量连接卡在 TIME_WAIT











