sendfile与tcp_nopush必须成对启用才能协同生效,单独开启任一指令均无效;二者共同实现内核零拷贝与TCP包合并,降低CPU开销、减少系统调用、提升单包载荷率。

sendfile 和 tcp_nopush 是一对必须成对启用、协同生效的优化指令,它们共同作用于静态文件传输路径,目标是减少 CPU 开销、降低系统调用次数、提升单 TCP 包载荷率。单独开启任一指令均无法达成预期效果。
零拷贝路径:sendfile 如何改变数据流向
默认情况下,Nginx 读取磁盘文件需经历“内核页缓存 → 用户态缓冲区 → socket 发送缓冲区 → 网卡”四次数据搬运。启用 sendfile 后,内核直接在两个文件描述符间完成复制(如从磁盘 fd 到 socket fd),跳过用户态内存拷贝和上下文切换。
- 仅对本地静态文件(如 location 匹配 .jpg、.mp4)或满足条件的代理响应生效
- 若启用了 gzip、sub_filter、proxy_buffering 或响应无 Content-Length,sendfile 自动禁用
- 推荐配置:sendfile on; 和 sendfile_max_chunk 512k;(避免大文件阻塞事件循环)
包合并机制:tcp_nopush 为何依赖 sendfile
tcp_nopush 的本质是让内核把响应头与文件起始数据“攒进一个 TCP 报文”再发出,前提是数据能被视作逻辑连续的整块——这只有在 sendfile 路径下才成立。普通 read/write 模式中数据分段进入发送队列,tcp_nopush 无法识别完整首块。
- 仅在 sendfile 生效时触发;否则 Nginx 日志会提示 “tcp_nopush is ignored”
- 需配合 tcp_nodelay off 使用(二者互斥:tcp_nodelay 强制立即发包,抵消“攒包”效果)
- 对 HTTPS 场景效果减弱,因 TLS 记录封装破坏了原始 TCP 包结构
精准启用:按资源类型做 location 级控制
全局开启易干扰 API、WebSocket 等低延迟服务。应依据 MIME 类型或后缀,在具体静态资源路径中显式启用:
- 图片类:
location ~* \.(jpg|jpeg|png|webp|gif|avif|svg)$ { sendfile on; tcp_nopush on; gzip off; } - 大文件类:
location ~ \.(mp4|zip|iso|tar\.gz|dmg)$ { sendfile on; tcp_nopush on; tcp_nodelay off; expires 7d; } - 务必关闭该 location 下的 gzip,防止压缩强制走用户态流程
- 若代理后端(如对象存储),需设 proxy_buffering off; 并确保后端返回 Content-Length
验证是否真正生效
不能只看配置是否存在,要从运行时行为确认:
- 用
strace -e trace=sendfile,write,writev -p $(pgrep nginx)观察 worker 是否调用 sendfile 系统调用 - 用 Wireshark 抓包,检查静态资源响应是否集中在 1–2 个 TCP 段内,且无大量小包(如 40–60 字节)
- 对比开启前后 top 中 nginx worker 的 CPU 占用率,高并发下应有明显下降


















