sendfile导致带宽打满、软中断飙升和丢包,本质是内核绕过用户态直接dma发送,缺乏流量整形与拥塞控制,引发突发流量压垮网络栈或链路。

启用 sendfile 传输大文件时带宽瞬间打满、网卡软中断飙升甚至丢包,本质是内核绕过用户态直接 DMA 发送,缺乏流量整形和拥塞控制,导致突发流量压垮网络栈或物理链路。
确认是否真由 sendfile 引发
先排除其他干扰因素:
- 用
ss -i或cat /proc/net/snmp | grep -A1 Tcp查看重传、乱序、SACK 丢包指标,若重传率突增,说明下游接收方处理不过来或网络中间有瓶颈 - 用
iftop -P 80或tcpreplay --stats(配合抓包)确认单连接是否持续跑满带宽,而非多连接叠加 - 临时关闭
sendfile on,改用sendfile off; aio on; output_buffers 1 128k;对比带宽曲线 —— 若毛刺消失,基本可锁定问题
限制 sendfile 的突发强度
Nginx 本身不提供 sendfile 速率限制,需靠内核参数协同压制:
-
启用 TCP 小包合并与延迟确认:
net.ipv4.tcp_nodelay = 0(默认),避免小 ACK 干扰;net.ipv4.tcp_delack_min = 10(毫秒),减少 ACK 频次 -
调低 TCP 发送窗口初始值:
net.ipv4.tcp_rmem = 4096 131072 6291456和net.ipv4.tcp_wmem = 4096 131072 4194304,缩小 wmem 上限能抑制单连接突发量 -
开启 fq 拥塞控制:
echo "fq" > /proc/sys/net/core/default_qdisc,配合tc qdisc add dev eth0 root fq,对每个流做公平排队,防单个大文件独占队列
用 proxy_buffering + limit_rate 替代粗暴 sendfile
对超大静态文件(如 >100MB),宁可牺牲一点零拷贝优势,换取可控性:
- 在 location 块中显式关闭 sendfile:
sendfile off; - 启用缓冲代理:
proxy_buffering on; proxy_buffer_size 128k; proxy_buffers 8 256k; - 按需限速:
limit_rate 50m;(单位为 bytes/sec,50m = 50MB/s),支持变量如$limit_rate = 20m动态调整 - 搭配
aio threads(需编译时加--with-threads)可降低阻塞,比纯阻塞 read() 更高效
检查硬件与驱动层瓶颈
即使协议层调优,底层仍可能拖后腿:
- 运行
ethtool -S eth0 | grep -i "drop\|over\|error",重点关注rx_missed_errors(Ring Buffer 溢出)、tx_aborted_errors(驱动丢包) - 增大 Ring Buffer:
ethtool -G eth0 rx 4096 tx 4096(数值依网卡型号而定,常见为 512/1024/4096) - 确认网卡是否启用了 LRO/GRO:
ethtool -k eth0 | grep gro,高吞吐场景建议ethtool -K eth0 gro off,避免大包重组加剧 CPU 负担 - 检查 IRQ 分布:
cat /proc/interrupts | grep eth0,若所有中断集中在单个 CPU 核,用irqbalance或手动绑核均衡负载











