要让nginx的sendfile跑出高吞吐,需协同linux page cache:预热大文件、调优vm参数、配置sendfile相关指令闭环、禁用压缩与动态头等干扰项,并用strace和fincore验证实际路径。

要让 Nginx 的 sendfile 真正跑出高吞吐,不能只开个开关,得让它和 Linux 文件系统缓存(page cache)协同工作——核心是让文件内容常驻内存、减少磁盘 I/O,同时确保 sendfile 路径不被干扰。
确保文件能进 page cache 并长期驻留
Linux 的 page cache 是 sendfile 高效的前提:如果每次都要从磁盘读块,零拷贝也白搭。关键不是“能不能缓”,而是“会不会被踢出去”:
- 大文件(如视频、安装包)建议用
posix_fadvise(POSIX_FADV_WILLNEED)预热,或启动时用cat file >/dev/null触发预读;Nginx 本身不提供自动预热,需运维脚本配合 - 避免频繁写入同目录:ext4/xfs 下大量小文件写入会触发 inode 更新和 journal 刷盘,间接挤占 page cache 空间
- 调大系统级缓存水位:
vm.vfs_cache_pressure=50(默认100),降低 dentry/inode 缓存回收优先级;vm.swappiness=1防止内存紧张时 page cache 被 swap 出去
配置 sendfile 相关参数形成闭环
单开 sendfile on 不够,必须堵住所有导致退化的路径,让内核始终走 sendfile 调用:
-
sendfile on;—— 全局开启,静态资源和 proxy_cache 落盘文件都可受益 -
tcp_nopush on;—— 和 sendfile 绑定,攒够 TCP MSS 再发包,避免小包碎片 -
tcp_nodelay off;—— 关闭 Nagle 算法,否则会和 tcp_nopush 冲突;仅对健康检查等极小响应的 location 单独设为 on -
sendfile_max_chunk 512k;—— 防止单次 sendfile 调用阻塞 worker,适合 1MB–100MB 级文件
绕过用户态干扰,守住零拷贝边界
很多配置看似无关,实则一触即溃 sendfile 路径:
- 禁用运行时压缩:
gzip off;,改用gzip_static on;提前生成 .gz 文件,仍走 sendfile - 不加动态头:移除
add_header X-Content-Digest $body_bytes_sent;类依赖响应体的指令,它们强制进入用户态 - 避开 Range 请求干扰:若需支持拖拽,确保不启用
etag on;或expires动态计算逻辑;MP4/HLS 分片本身支持 Range,无需额外干预 - 禁用 sub_filter、proxy_buffering:这些模块必然读取并修改响应流,直接退化为 read/write
验证是否真在用 page cache + sendfile
别信配置,要看内核行为:
- 用
strace -p $(pgrep nginx) -e trace=sendfile持续观察,看到高频 sendfile 系统调用,说明路径畅通 - 查 page cache 命中率:
cat /proc/meminfo | grep -i "cached\|buffers",再结合fincore --pages=false --summarize --only-cached /path/to/file确认目标文件是否已缓存 - 对比测试:关闭 sendfile 后用
perf top -p $(pgrep nginx),若sys_read和sys_write占比飙升,就证实了优化价值











