Nginx平滑重载后“Too many open files”未缓解,说明旧worker未优雅退出或新worker继承残留句柄;需检查旧进程PID是否存活、lsof确认fd滞留、/proc/*/stack定位卡点,并核查worker_shutdown_timeout、keepalive_timeout与upstream超时匹配性,以及open_log_file_cache和ssl_session_cache配置合理性。

平滑重载(nginx -s reload)本应让旧 worker 进程优雅退出、释放全部句柄,但若重载后 Too many open files 错误未缓解,甚至句柄数持续攀升,说明旧进程未真正退出或新进程继承/累积了残留资源。排查需聚焦“重载是否真完成”和“哪些句柄卡在旧进程没关”。
确认旧 worker 是否已退出
重载不是杀进程,而是通知 master 启动新 worker、等待旧 worker 处理完请求再退出。若旧 worker 挂起或卡住,fd 就一直占着:
- 执行
ps aux | grep "nginx: worker",对比重载前后 worker 进程 PID 数量——若旧 PID 仍存在且TIME列持续增长,说明未退出 - 查旧 worker 的打开数:
lsof -p [old_pid] | wc -l,若数值稳定不降(比如始终 >5000),基本确认滞留 - 检查
/proc/[old_pid]/stack,看是否卡在epoll_wait或recv等系统调用,常见于后端响应超时、keepalive 连接未断开
检查重载期间的连接生命周期配置
很多“不释放”源于配置未对齐,导致连接被拖长、fd 无法回收:
-
worker_shutdown_timeout 未设或过短:默认为 0(无限等待),但若设为 2s 之类,旧 worker 可能被强制 kill,fd 来不及清理。建议显式设为
60s,给足时间处理完活跃连接 -
keepalive_timeout 和后端
proxy_read_timeout不匹配:例如 Nginx keepalive 设 60s,但 upstream 服务只保持 10s 连接,Nginx 会以为连接还有效,迟迟不关 fd。应确保 upstream 超时 ≥ Nginx keepalive - proxy_next_upstream_timeout 过大:当后端失败重试时,整个请求生命周期拉长,关联 fd 也延迟释放
抓取重载瞬间的 fd 状态差异
光看总量不够,要定位新增的是哪类句柄:
- 重载前执行:
lsof -p $(pgrep -f "nginx: worker" | head -1) | awk '{print $5}' | sort | uniq -c | sort -nr | head -10,记录 socket、REG(文件)、IPv4 等类型分布 - 重载后 30 秒再执行同样命令,重点比对:socket 数是否翻倍?大量
DEL(已删除但未关闭的文件)或IPv4状态为TIME_WAIT的条目是否激增? - 若发现大量
REG类型且路径含access.log或error.log,说明日志轮转未触发句柄关闭,需检查open_log_file_cache是否启用或日志路径含变量
验证重载后的新 worker 是否干净启动
有时新 worker 一启动就带高 fd 数,说明它从父进程继承了未清理资源或配置加载异常:
- 等旧 worker 全退出后,挑一个新 worker PID,立即执行
lsof -p [new_pid] | wc -l,理想值应 ≤worker_connections × 1.1(含少量日志、监听套接字等) - 若新 worker 启动即达 5000+,检查
open_file_cache是否开启且inactive时间过大(如 300s),静态资源缓存未淘汰,fd 被长期持有 - 确认
ssl_session_cache使用共享内存(如shared:SSL:10m),而非builtin——后者每次握手都分配新 fd,重载后易堆积


















