开启 sendfile 后,Nginx 仍需完成 TLS 加解密(用户态),零拷贝与 TLS 处理独立;启用 kTLS(Linux ≥4.13 + OpenSSL ≥1.1.1k + Nginx ≥1.21.4)可让静态文件在内核中直接加密传输,实现零拷贝与 TLS 加速协同。

开启 sendfile 后,Nginx 仍需完成 TLS 加解密,而加解密发生在用户态(除非启用 kTLS),因此 sendfile 的零拷贝优势与 TLS 处理是两个独立环节——前者优化数据搬运路径,后者决定加密开销。排查加解密效率,核心是确认 CPU 是否被 TLS 协商或对称加解密拖慢,而非怀疑 sendfile 失效。
确认是否真正走零拷贝路径
即使配置了 sendfile on,只要启用了 gzip on、sub_filter 或响应为 chunked 编码,Nginx 就会自动退回到 read()+write() 模式,此时不仅失去零拷贝,还叠加了压缩/改写开销,进一步放大 TLS 负担。
- 用
strace -p $(pgrep nginx -f | head -1) -e trace=sendfile64,read,write观察 worker 进程:若高频出现sendfile64调用,说明零拷贝生效;若只有read和write,说明已退化 - 检查 Nginx 配置中 HTTPS 的
location块是否禁用了gzip、gunzip和所有内容改写指令 - 确保静态文件是本地 ext4/XFS 文件系统上的普通文件,且未被其他进程加锁或以
O_DIRECT方式打开
定位 TLS 层的 CPU 瓶颈点
TLS 性能瓶颈通常集中在密钥协商(非对称运算)和记录加密(对称运算)。SSL/TLS 握手阶段的 CPU 消耗远高于后续数据传输阶段,尤其在高并发短连接场景下更明显。
- 运行
pidstat -u -p $(pgrep nginx) 1,观察各 worker 进程的 %sy(系统态)和 %us(用户态)占比:若 %us 持续高于 60%,说明 OpenSSL 解密/签名等计算密集操作占主导 - 执行
openssl speed ecdhp256 rsa2048对比本地 CPU 的 ECC 与 RSA 吞吐:若ecdhp256显著更快(通常快 3–5 倍),应在ssl_ciphers中优先放置ECDHE-*套件,并确保ssl_prefer_server_ciphers on - 检查
ssl_protocols是否包含 TLSv1.3:该协议大幅简化握手流程(1-RTT 或 0-RTT),可降低 30%+ 握手 CPU 开销;需 OpenSSL ≥ 1.1.1f + Nginx ≥ 1.17.0
验证 kTLS 是否启用(Linux 内核级加速)
从 Nginx 1.21.4 起,配合支持 kTLS 的内核(Linux ≥ 4.13)和 OpenSSL(编译时启用 kTLS 支持),可让静态文件在内核中直接加密后发往网卡,彻底绕过用户态加解密——这是真正将 sendfile 与 TLS 加速结合的方案。
- 确认内核支持:
zcat /proc/config.gz | grep CONFIG_TLS或检查/proc/sys/net/ipv4/tcp_tls_ulp是否存在 - 检查 Nginx 编译参数是否含
--with-http_ssl_module --with-openssl=...且 OpenSSL 版本支持 kTLS(≥ 1.1.1k 推荐) - 启用后,用
ss -i查看连接状态,若输出中出现tls字样,表示 kTLS 已激活 - 对比开启前后 CPU 的 %us 使用率:kTLS 可使 TLS 加密阶段的用户态 CPU 下降 40%–70%,尤其对大文件 HTTPS 传输提升显著
配套调优避免干扰零拷贝与 TLS 效率
一些看似无关的配置,实际会间接拉高 TLS 处理负担或阻塞 sendfile 流程。
-
tcp_nodelay off必须与tcp_nopush on共存:若误设tcp_nodelay on,会强制拆包,导致小 TCP 报文激增,加重 TLS 记录分片与加密次数 -
sendfile_max_chunk 512k应设为合理值:过大可能阻塞事件循环,影响新连接握手;过小则增加系统调用频次,间接抬高 CPU - 关闭静态资源 location 的
access_log:高并发下日志 I/O 会竞争 CPU 和磁盘带宽,掩盖真实的 TLS 效率问题 - 使用
open_file_cache缓存文件句柄:减少重复open()/stat()系统调用,让 CPU 更专注处理 TLS 计算


















