keepalive_timeout过长会导致空闲连接长期驻留,引发worker内存RSS线性缓慢攀升;因每个连接独占socket、缓冲区及SSL上下文等资源,且释放滞后于建立,故表现为每小时稳定增长20–50MB,HTTPS下尤为显著。

当 Nginx 的 keepalive_timeout 设置过长(如 300 秒),空闲连接会长期驻留,导致 worker 进程内存 RSS 持续缓慢攀升——这不是瞬时暴涨,而是随并发连接数线性累积的“慢性内存占用”。问题本质是每个空闲连接仍需保有 socket 控制块、读写缓冲区(默认各 8KB–64KB)、HTTPS 下的 SSL 会话上下文等 per-connection 资源,而这些不会被共享或复用。
一眼识别是否由 keepalive_timeout 过长引发
不用等 OOM,几个轻量命令就能快速定位:
-
看连接堆积:执行
ss -s,重点关注tcp: xxx行中 ESTABLISHED 数量。若该值远高于实际活跃请求数(例如压测中 QPS 2000,但 ESTABLISHED 稳定在 8000+),且多数连接处于空闲状态,大概率是 timeout 太长 -
盯 Waiting 状态:开启
stub_status,观察Waiting值。若它长期占 Active connections 总数的 60% 以上,说明大量连接在“空转”,既没被复用也没释放 -
查内存增长趋势:运行
ps aux --sort=-%mem | grep nginx,持续观察 worker 进程 RSS。若在无流量突增情况下,RSS 每小时稳定上升 20–50MB,尤其 HTTPS 服务中单连接开销达普通 HTTP 的 3–5 倍,基本可锁定
为什么不是“连接越多越快撑爆”,而是“缓慢攀升”
因为内核和 Nginx 的资源回收是渐进式的:
- 空闲连接不触发立即释放,只在超时后由事件循环统一处理;高并发下超时时间错峰,释放节奏滞后于建立节奏
- SSL session ticket、密钥材料等 TLS 上下文在连接关闭前不会被 GC,且部分 OpenSSL 版本存在缓存未及时清理现象
- 文件描述符(fd)先耗尽:
ulimit -n限制固定,timeout 越长,fd 被空闲连接长期占用,新连接 accept 失败 → Nginx 可能启动临时 retry 逻辑或 fallback 进程,间接抬高内存与 CPU
验证与收口动作
调优不是改完就结束,必须闭环验证:
- 将
keepalive_timeout从 300 秒逐步下调至 30 秒(配合keepalive_requests 1000),观察 24 小时内ss -s中 ESTABLISHED 峰值是否回落至合理区间(建议 ≤ 并发请求峰值 × 2) - 检查日志中
$connection_requests字段:若大量连接仅承载 1–2 个请求就超时关闭,说明客户端行为(如 App 休眠)与 timeout 不匹配,需进一步缩短 - 确认上游负载均衡器(如 ALB、Nginx Ingress)的 keepalive 超时 ≥ Nginx 设置,否则连接会在中间层被提前断开,造成
CLOSE_WAIT堆积和响应毛刺
配套必须同步调整的配置
单独改 keepalive_timeout 效果有限,以下三项需一并落实:
-
keepalive_requests 100–1000:防止单连接无限承载请求,达到上限即刻关闭,不等超时 -
upstream { keepalive 32; }+proxy_http_version 1.1;+proxy_set_header Connection '';:确保反向代理链路真正复用,避免后端重复建连加重压力 -
reset_timedout_connection on;:对已超时但尚未关闭的异常连接快速发送 RST,加速 fd 和内存回收











