Nginx 缓冲区调优:优化 `client_body_buffer_size` 和 `proxy_buffer_size`
在不浪费内存或导致可避免的磁盘缓冲的情况下,调优 Nginx 请求和代理缓冲区。
Nginx 缓冲区调优:优化 client_body_buffer_size 和 proxy_buffer_size
Nginx 缓冲区调优并非要将每个缓冲区都设置得巨大。关键在于决定哪些数据应驻留在内存中,哪些数据可以安全地溢出到磁盘,以及哪些响应应该流式传输而非完全缓冲。
令人困惑的是,有几个指令听起来很相似。client_body_buffer_size 适用于来自客户端的请求体。proxy_buffer_size 和 proxy_buffers 适用于当 Nginx 作为反向代理时,来自上游服务器的响应。FastCGI 有其自己的匹配指令。如果你调优了错误的一侧,则不会有任何改善。
一次好的调优会话从症状开始:
- Nginx 错误日志提到临时文件。
- 上传或大型 POST 请求感觉缓慢。
- 反向代理的 API 响应在负载下暂停。
- Worker 进程使用的内存超出预期。
- 大型响应头触发上游错误。
这些症状并非都有相同的修复方法。更大的缓冲区可以减少磁盘 I/O,但它们也会增加每个请求的内存压力。在一个繁忙的站点上,每个请求看似很小的设置,在乘以数千个并发连接后可能会变得很大。
请求体缓冲:client_body_buffer_size
client_body_buffer_size 控制 Nginx 读取客户端请求体时使用的缓冲区。考虑表单提交、JSON POST 体和上传。如果请求体大于此缓冲区,Nginx 可能会将部分内容写入 client_body_temp_path 下的临时文件。
一个基本设置如下所示:
http {
client_body_buffer_size 128k;
}
这不会改变允许的最大上传大小。这由 client_max_body_size 控制:
server {
client_max_body_size 20m;
client_body_buffer_size 128k;
}
这两个指令回答了不同的问题。client_max_body_size 表示:“在 Nginx 拒绝请求之前,请求可以有多大?” client_body_buffer_size 表示:“在使用临时文件之前,Nginx 应将多少请求体保留在内存中?”
对于一个大多数请求体小于 32 KB 且少数为 200 KB 的 JSON API,128k 的缓冲区可以在不过度的情况下减少临时文件的写入。对于一个接受 50 MB 视频的文件上传服务,全局设置 client_body_buffer_size 50m 将是一个糟糕的权衡。你会为一个磁盘缓冲可能可接受的工作负载预留太多内存。
检查错误日志以获取类似线索:
[warn] a client request body is buffered to a temporary file
该警告本身并不表示失败。这是 Nginx 在告诉你请求体超过了内存缓冲区。如果它偶尔发生在大型上传期间,没问题。如果它经常发生在普通 API 请求上,请调优缓冲区或修复发送大型负载的客户端。
代理响应缓冲:proxy_buffer_size 和 proxy_buffers
当 Nginx 代理到上游应用程序时,响应缓冲通常默认启用。Nginx 快速读取上游响应,将其存储在缓冲区中,然后发送给客户端。如果响应不适合内存缓冲区,则部分内容可能会写入临时文件。官方 Nginx 代理模块文档描述了此行为,并将临时文件写入与 proxy_max_temp_file_size 和 proxy_temp_file_write_size 联系起来。
第一个缓冲区由 proxy_buffer_size 控制:
proxy_buffer_size 16k;
此缓冲区处理响应头和响应的第一部分。如果你的上游发送大型头部,例如许多 cookie、大型认证头或过大的跟踪元数据,你可能会看到类似“upstream sent too big header”的错误。在这种情况下,通常需要审查 proxy_buffer_size 指令。
其余的响应缓冲区由 proxy_buffers 控制:
proxy_buffers 8 32k;
这意味着八个 32 KB 的缓冲区用于代理响应数据。这并不意味着每个请求始终从头到尾消耗全部容量,但在负载下,你仍应将这些视为每个请求的内存设置。
一个常见的反向代理块如下所示:
location /api/ {
proxy_pass http://app_backend;
proxy_buffering on;
proxy_buffer_size 16k;
proxy_buffers 8 32k;
proxy_busy_buffers_size 64k;
}
不要盲目复制此设置。小型 HTML 站点、JSON API 和文件下载服务有不同的需求。
临时文件和 proxy_max_temp_file_size
如果启用了代理响应缓冲,并且响应大于配置的缓冲区,Nginx 可能会将部分内容写入磁盘。proxy_max_temp_file_size 限制该临时文件的大小。Nginx 的默认值通常记录为 1024m,将其设置为 0 会禁用将代理响应缓冲到临时文件。
location /api/ {
proxy_pass http://app_backend;
proxy_buffering on;
proxy_max_temp_file_size 50m;
}
谨慎使用 0:
proxy_max_temp_file_size 0;
这并不意味着大型响应变得免费。这意味着 Nginx 不会为这些缓冲的代理响应使用临时文件。根据响应、客户端速度和缓冲行为,你可能需要更多内存、不同的缓冲设置或流式设计。
来自 Nginx 文档的一个重要细微差别:proxy_max_temp_file_size 限制不适用于将被缓存或存储在磁盘上的响应。如果你使用 proxy_cache 或 proxy_store,请分别审查这些设置。
何时 proxy_buffering off 是更好的答案
有时你根本不应该缓冲响应。流式端点、服务器发送事件、大型下载和长时间运行的输出通常在使用代理缓冲禁用时效果更好:
location /events/ {
proxy_pass http://app_backend;
proxy_buffering off;
}
关闭缓冲后,Nginx 在接收到上游响应时立即将其传递给客户端,而不是尝试收集完整的响应。proxy_buffer_size 仍然重要,因为 Nginx 需要一个缓冲区来接收来自上游的数据,但 proxy_buffers 和临时文件行为变得不那么重要。
除非你了解权衡,否则不要全局关闭缓冲。缓冲保护上游服务器免受慢速客户端的影响。如果客户端下载缓慢且缓冲关闭,上游连接可能会被占用更长时间。
用于 PHP 和类似设置的 FastCGI 缓冲区
如果 Nginx 与 PHP-FPM 通信,则不需要 proxy_* 指令。FastCGI 使用匹配的名称:
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_buffer_size 16k;
fastcgi_buffers 8 32k;
}
同样的逻辑适用:第一个缓冲区处理头部和初始数据;缓冲区数组处理更多响应内容。如果 PHP 发送大型头部,可能需要关注 fastcgi_buffer_size。如果大型 PHP 生成的页面溢出到磁盘,请审查 fastcgi_buffers 和工作负载。
如何在不猜测的情况下调优
首先检查日志:
sudo tail -f /var/log/nginx/error.log
查找关于请求体或上游响应被缓冲到临时文件的警告。然后测量实际涉及的大小。对于 API,从访问日志、应用程序日志或你的可观测性系统中采样请求和响应大小。对于上传,将元数据请求与文件上传端点分开。它们不应共享相同的假设。
在最有意义的狭窄上下文中进行调优。不要这样:
http {
client_body_buffer_size 10m;
}
而是仅对上传端点采用类似这样的设置:
location /upload/ {
client_max_body_size 50m;
client_body_buffer_size 512k;
proxy_pass http://upload_backend;
}
对于偶尔有 1 MB 响应的 JSON API,你可能只调优该 API 位置:
location /reports/ {
proxy_pass http://report_backend;
proxy_buffering on;
proxy_buffer_size 32k;
proxy_buffers 16 64k;
proxy_max_temp_file_size 20m;
}
然后计算最坏情况。如果 1,000 个并发请求每个可能使用几百 KB 的响应缓冲区,那就是真实的内存。为 worker 进程、TLS、上游连接、缓存、操作系统页面缓存和其他服务留出空间。
验证配置并监控系统
每次更改后:
sudo nginx -t
sudo systemctl reload nginx
然后监控内存、磁盘 I/O 和错误日志。成功的语法测试仅证明文件可以解析。它不能证明这些值是正确的。
有用的检查包括:
free -h
iostat -xz 1
sudo tail -f /var/log/nginx/error.log
如果临时文件警告消失但内存压力上升,则你调优过度了。如果内存保持健康且受影响工作负载期间的磁盘 I/O 下降,则更改可能有所帮助。
Nginx 缓冲区调优在本地化、可测量且与实际流量相关时效果最佳。在节省延迟的情况下将普通请求保留在内存中,让真正的大型上传或下载使用正确的路径,并避免使每个连接都为最大的边缘情况付出代价的全局设置。