keepalive_timeout设太长会随空闲连接数线性堆积资源,每个多占1个fd和4–12KB内存,不耗CPU但拖垮内存与文件描述符,高并发易致雪崩。

keepalive_timeout 设得太长,不会按“固定数值”占用资源,而是会随着空闲连接数线性堆积——每多一个挂起的空闲连接,就多占一份资源。它不直接吃 CPU,但悄悄拖垮内存和文件描述符(fd),高并发下极易触发雪崩。
每个空闲连接实际占多少资源?
一个典型的 HTTP/1.1 空闲 keep-alive 连接,在 Nginx worker 进程中大致消耗:
-
1 个文件描述符(fd):每个连接独占一个 fd,受
worker_connections和系统ulimit -n限制; -
约 4–12 KB 内存:包括 socket 结构体、读写缓冲区(
client_header_buffer_size、client_body_buffer_size)、SSL 上下文(HTTPS 下更重)、连接状态跟踪等; - 少量内核态开销:如 TCP 套接字状态(ESTABLISHED)、TIME_WAIT 潜在风险(若客户端异常断连);
-
无 CPU 占用:空闲连接不触发事件循环,但大量 fd 会拖慢
epoll_wait()或kqueue的轮询效率。
资源堆积的真实影响场景
假设你设 keepalive_timeout 300;(5 分钟),单个 worker 进程配置 worker_connections 1024,系统 ulimit -n 为 65536:
- 若同时有 800 个用户保持空闲连接(常见于后台系统或弱网 App),仅这部分就占掉近 800 个 fd 和 ~6–8 MB 内存;
- 当活跃请求突增时,新连接无法分配 fd,Nginx 返回
503 Service Temporarily Unavailable或系统报Too many open files; - 监控中
Active connections的Waiting状态持续 >80%,说明大部分连接在“假待命”,实际复用率极低; - 内存 RSS 持续爬升,压测时可能触发 OOM Killer 杀掉 worker 进程。
不是“单个连接很贵”,而是“积少成多”
单独看一个空闲连接,资源开销微乎其微;但它的危害在于:它不释放、不告警、不主动退出,只安静地排队等着被遗忘。尤其在以下情况放大风险:
- 移动端 App 批量请求后进入 10–30 秒休眠,timeout 设 60s 就几乎全断,设 300s 则全部滞留;
- 前端用了多个域名加载资源(如 cdn1.example.com、cdn2.example.com),每个域名都建独立连接池;
- 中间有 NAT 网关或企业防火墙(默认 2–5 分钟回收),Nginx 设再长也白搭,反而让连接卡在“半死不活”状态;
- 未配
keepalive_requests,某个慢请求或大文件下载长期占着连接,使 timeout 形同虚设。
怎么判断是不是已经占多了?
别猜,看真实指标:
-
ss -s | grep "TCP:"查当前 ESTABLISHED 总数,对比峰值业务量是否异常偏高; -
nginx -T 2>/dev/null | grep -A2 'http {' | grep worker_connections确认理论上限; - 启用
stub_status,观察Reading/Writing/Waiting中Waiting是否长期 >50%; - 日志加
$connection_requests $request_time,统计单连接平均间隔 —— 若中位数远大于 timeout,说明设长了没意义。



















