tcp_nopush 与 sendfile 协同优化静态文件传输:前者在 sendfile 启用时攒包发送以减少小包,后者通过零拷贝降低 CPU 和内存开销;二者必须同时开启才生效,否则 tcp_nopush 被忽略。

在 Nginx 中,tcp_nopush 和 sendfile 是两个常被一起启用、协同优化静态文件传输性能的关键指令。它们的作用不是孤立的,而是通过减少系统调用和网络包数量,让内核更高效地把文件内容推送到客户端。
tcp_nopush 的作用与触发条件
tcp_nopush(仅适用于 FreeBSD 和 Linux)控制是否启用 TCP 的 TCP_NOPUSH(FreeBSD)或 TCP_CORK(Linux)选项。开启后,Nginx 会尽量把多个小响应头或响应体“攒”成一个完整的 TCP 包再发出,避免小包泛滥(即 Nagle 算法的补充机制)。
但它只在满足以下条件时才真正生效:
- 必须配合
sendfile on使用(否则无意义) - 响应体由内核直接从文件描述符发送(即走零拷贝路径)
- 响应头已写入,且后续要发的数据能填满或接近 MSS(最大分段大小)
简单说:tcp_nopush on 不是“强制延迟发送”,而是“等一等,凑够了再发”,前提是底层走的是 sendfile。
sendfile 如何改变数据流路径
sendfile 开启后,Nginx 不再把文件内容从内核页缓存读到用户空间缓冲区,再 write 到 socket;而是直接由内核完成“文件 → socket”的搬运,省去两次内存拷贝和一次用户态上下文切换。
典型效果:
- CPU 使用率下降(尤其大文件、高并发场景)
- 内存带宽压力减小(避免用户空间 buffer 占用)
- 为
tcp_nopush提供了“可攒包”的前提(因为数据不经过用户态,内核能统一调度)
注意:若启用了 gzip on 或任何需要用户态处理的过滤模块(如 sub_filter),sendfile 会自动失效,tcp_nopush 也退化为无效配置。
推荐配置与典型陷阱
标准高性能静态服务配置应类似:
http {
sendfile on;
tcp_nopush on;
tcp_nodelay off; # 与 tcp_nopush 协同:禁用 Nagle 对小交互的影响,但大文件传输中通常保持 off
keepalive_timeout 65;
}
常见误配情况:
-
sendfile on但tcp_nopush off:失去包合并机会,小文件可能产生大量 40–60 字节的 TCP 包 -
tcp_nopush on但sendfile off:该指令被忽略(Nginx 日志中会出现 warning:“tcp_nopush is ignored”) - 在启用了
etag、expires等头部操作的同时,未确认是否触发了 sendfile 禁用(实际不影响,这些只是 header 操作) - 反向代理场景下错误开启 sendfile:后端响应非本地文件,
sendfile不生效,此时tcp_nopush也无效
如何验证是否生效
可通过以下方式交叉验证:
- 用
strace -e trace=sendfile,write,sendto跑 Nginx worker,观察是否调用sendfile()系统调用 - 用
ss -i查看连接的 TCP 队列状态,高负载下若cwnd稳定上升且重传率低,说明包合并有效 - 抓包分析(如 Wireshark):对同一静态资源多次请求,观察 TCP payload size 是否趋近于 MSS(如 1448 字节),而非零散小包
- Nginx error log 中没有 “tcp_nopush is ignored” 类警告
真实压测中,在千兆内网环境,开启组合后,1MB 文件的平均响应时间可降低 8%~12%,QPS 提升约 15%(取决于磁盘 I/O 和 CPU 负载均衡程度)。










