确认是文件句柄耗尽,需分层排查:若仅部分worker报错,可能是fd泄漏;若全部同步报错且dmesg提示file-max告警,则为内核级总量不足;应立即检查/proc/sys/fs/file-nr、各进程limits及lsof分布,并同步调高sysctl、limits.conf、systemd LimitNOFILE和nginx worker_rlimit_nofile。

看到 error_log 里反复出现 “accept() failed (24: Too many open files)”,基本可确认是文件句柄(fd)耗尽引发的读写异常。但这个报错只是结果,不是原因——它可能来自系统、用户、systemd 或 Nginx 自身任意一层限制未对齐,不能只改一个配置就完事。
先让错误日志真正“说话”
Nginx 默认 error_log 级别为 error,而 fd 耗尽属于内核级失败,需 alert 或更高级别才能稳定记录。若日志中只有零星 error 却没 alert,说明日志等级不够:
- 在 nginx.conf 的 main 上下文(http 块外)添加:
error_log /var/log/nginx/error.log alert; - 执行
nginx -t && nginx -s reload生效 - 避免长期开启 debug:日志量爆炸,且不解决根本问题
验证 Nginx 进程实际生效的 fd 限制
别信 ulimit -n,systemd 启动的服务不会读取 shell 的 limits。必须查真实运行值:
- 用
systemctl show --property MainPID nginx获取主进程 PID - 运行
cat /proc/<pid>/limits | grep "Max open files"</pid>,看 Soft Limit 值 - 再执行
ls -l /proc/<pid>/fd/ 2>/dev/null | wc -l</pid>,对比实时占用是否逼近该值 - 若占用接近 Soft Limit,说明进程级已卡死;若远低于却仍报错,要查 nr_open 内核参数或 memfd 异常
分层排查 fd 耗尽的真实位置
不能只盯着 Nginx 配置,要从底向上看:
-
系统层:运行
cat /proc/sys/fs/file-nr,看第二列(当前使用)是否超过第三列(fs.file-max)的 80%;再跑dmesg | grep -i "file.*max"确认有无内核告警 -
用户层:查 Nginx 实际运行用户(如 www-data),用
sudo -u www-data bash -c 'ulimit -Sn'看其软限制是否生效 -
systemd 层:编辑
/etc/systemd/system/nginx.service.d/override.conf,加[Service]LimitNOFILE=65536,然后systemctl daemon-reload && systemctl restart nginx -
Nginx 层:在 nginx.conf main 块中显式设
worker_rlimit_nofile 65536;,该值不能超用户 hard nofile 限制
定位具体泄漏源或高消耗点
报错只是表象,得看哪些 fd 没释放:
- 若仅个别 worker(如 PID 1234、5678)持续报错,其他正常 → 很可能是该进程内部泄漏:upstream keepalive 配置不当、open_file_cache 缓存了太多文件、Lua 脚本未 close socket、日志按小时切分却未 reload 导致句柄堆积
- 若所有 worker 同步报错,且
/proc/sys/fs/file-nr第二列飙升 → 是全局资源不足,优先调fs.file-max和用户 limits - 用
lsof -p <pid> | awk '{print $9}' | sort | uniq -c | sort -nr | head -10</pid>查该 worker 打开最多的路径,常见高耗场景:大量 socket:[xxx](连接堆积)、proxy_temp 下 deleted 文件(磁盘空间异常上涨)、access.log 每次请求都 open/write/close(缺 buffer/flush)


















