sendfile本身不导致句柄泄露,但会放大open_file_cache配置不当、日志路径动态化等底层缺陷,引发fd堆积;需重点检查缓存策略、日志路径及错误处理配置。

启用 sendfile on 本身不会直接导致句柄泄露,但若与其他配置或运行环境不匹配,可能间接引发句柄数持续上升——尤其在高并发静态文件服务场景下。问题核心往往不在 sendfile 本身,而在于它放大了底层资源管理的缺陷。
确认 sendfile 是否真是诱因
先排除干扰:sendfile 是内核态零拷贝机制,不打开新文件句柄,只复用已打开的文件 fd。因此:
- 如果
lsof -p [worker_pid] | grep REG显示大量重复的静态文件(如/usr/share/nginx/html/logo.png)长期驻留且不释放,说明是open_file_cache配置不当,不是 sendfile 的锅 - 如果
lsof -p [worker_pid] | grep socket中大量连接处于CLOSE_WAIT或ESTABLISHED但无对应请求日志,说明连接未正常关闭,与 sendfile 无关 - 真正需警惕的是:启用 sendfile 后,
access_log路径含变量(如$host或$request_uri),且日志文件被高频、多路径写入——此时每个新路径都会打开一个新 fd,sendfile 加速了请求处理,反而让日志开销更明显
重点检查 open_file_cache 与 sendfile 的协同
sendfile 依赖已打开的文件描述符,而这些 fd 由 open_file_cache 管理。若缓存策略松散,fd 就会长期滞留:
-
open_file_cache max=10000 inactive=300s:若业务每秒访问数百个不同小图标(favicon.ico、icon-16.png 等),300 秒内缓存不淘汰,fd 数会线性堆积 - 必须搭配
open_file_cache_valid 30s和open_file_cache_min_uses 2,避免短命文件进缓存 - 务必开启
open_file_cache_errors on,否则 404 文件也会占着 fd 不放 - 建议将
inactive从默认 300s 降至 20–60s,尤其在 CDN 回源或微前端场景下
排查日志路径动态化引发的隐性开销
当 access_log 使用变量时,Nginx 每次匹配都尝试打开新文件,sendfile 提升吞吐后,这一行为被加速暴露:
- 错误写法:
access_log /var/log/nginx/$host-access.log;→ 每个子域都开一个 fd - 正确做法:统一路径 +
open_log_file_cache缓存句柄,例如:
access_log /var/log/nginx/access.log;
open_log_file_cache max=1000 inactive=10s min_uses=5 valid=1m; - 若必须按域名分离日志,改用 logrotate + 定时归档,而非运行时动态路径
验证与收尾建议
调整后观察是否见效,关键看三组数据是否同步回落:
-
lsof -p $(pgrep -f "nginx: worker" | head -1) | wc -l—— 5 分钟内波动 ≤ ±20 -
find /proc/$(pgrep nginx | head -1)/fd/ -type l -exec ls -l {} \; 2>/dev/null | grep REG | wc -l—— 静态文件 fd 占比应 - Nginx error 日志中不再出现
open() "/path" failed (24: Too many open files)


















