Nginx 性能调优:优化工作进程与连接数
无需猜测即可调整 Nginx 工作进程、工作连接数、文件限制、keepalive 行为及上游容量。
Nginx 性能调优:优化工作进程与连接数
Nginx 性能调优通常从两个设置开始:工作进程(worker processes)和工作连接数(worker connections)。这些设置决定了服务器能同时处理多少请求,因此小错误可能在流量高峰时表现为页面加载缓慢、下载停滞或连接错误。
好消息是,你不需要盲目猜测。你可以通过将 Nginx 的工作模型与 CPU、文件限制、流量模式以及上游应用行为相匹配来进行调优。
Nginx 工作进程如何处理流量
Nginx 使用一个主进程和一个或多个工作进程。主进程读取配置、启动工作进程并处理重载。工作进程则负责实际的请求处理。
每个工作进程可以处理大量连接,因为 Nginx 采用事件驱动模型。这与旧式 Web 服务器不同,后者通常需要为每个请求分配一个进程或线程。如果操作系统允许,一个 Nginx 工作进程可以保持数千个空闲的 keepalive 连接。
两个核心指令通常位于 /etc/nginx/nginx.conf 中:
worker_processes auto;
events {
worker_connections 1024;
}
worker_processes auto; 告诉 Nginx 为每个可用的 CPU 核心创建一个工作进程。对于大多数现代 Linux 服务器来说,这是一个正确的起点。它避免了硬编码一个在调整虚拟机大小时会失效的值。
worker_connections 设置每个工作进程可以打开的最大同时连接数。粗略的上限是:
worker_processes * worker_connections
如果你有 4 个工作进程和 4096 个工作连接数,理论最大值是 16,384 个连接。在实际中,可用数量会更低,因为反向代理流量可能同时使用客户端连接和上游连接。
例如,如果 Nginx 将流量代理到 Node.js 应用,一个用户请求可能消耗一个客户端连接和一个上游连接。这意味着 16,384 个打开的连接可能支持大约 8,000 个活跃的代理请求,具体取决于 keepalive 和请求时序。
选择工作进程和连接数值
除非你有特定理由不这样做,否则从 worker_processes auto 开始。手动将此值设置为高于 CPU 数量通常没有帮助。它可能增加上下文切换,并在负载下使性能更差。
然后根据预期的并发量调整 worker_connections。一个安静的内部工具可能 1024 就足够了。一个位于负载均衡器后面的公共网站可能需要 4096、8192 或更多。
对于许多生产服务器,一个实用的基线如下所示:
worker_processes auto;
worker_rlimit_nofile 65535;
events {
worker_connections 4096;
multi_accept on;
}
worker_rlimit_nofile 提高了 Nginx 工作进程可用的文件描述符限制。这很重要,因为每个网络套接字都使用一个文件描述符。如果操作系统限制保持较低,仅增加 worker_connections 将无济于事。
你还应该检查服务管理器限制。在 systemd 系统上,Nginx 可能需要一个覆盖,例如:
[Service]
LimitNOFILE=65535
更改 systemd 限制后,重新加载 systemd 并重启 Nginx。有关更广泛的命令参考,请参阅 Nginx 服务控制命令。
小心使用 multi_accept on。它允许工作进程在收到就绪通知后尽可能多地接受新连接。这可以在突发情况下有所帮助,但并非魔法。如果你的上游应用速度较慢,更快地接受连接可能只会更快地填满队列。
影响 Nginx 的操作系统限制
Nginx 设置建立在 Linux 限制之上。如果这些限制太小,即使 Nginx 自身的配置看起来很慷慨,它也会达到瓶颈。
调优时检查以下方面:
- Nginx 进程的打开文件限制
- 内核网络积压设置
- 用于大量代理流量的临时端口可用性
- 上游 keepalive 行为
- 负载均衡器空闲超时值
打开文件限制是最常见的障碍。如果 Nginx 记录类似 worker_connections are not enough 或 too many open files 的消息,你需要同时检查 Nginx 和 systemd 限制。
当许多客户端同时连接时,积压设置很重要。如果内核接受队列已满,用户可能会看到连接超时,即使 CPU 使用率看起来正常。在高流量调优期间,通常会审查诸如 net.core.somaxconn 和 net.ipv4.tcp_max_syn_backlog 之类的值。
不要未经测试就从随机示例中复制大的内核值。一个小团队运行一个 API 服务器不需要与 CDN 边缘节点相同的设置。逐步调优,测量,并做好记录。
还有另一个细节会让人困惑:Nginx 连接限制并不是路径中唯一的连接限制。云负载均衡器有空闲超时。容器运行时可能有网络地址转换限制。后端应用可能有自己的工作池或数据库连接池。如果 Nginx 可以接受 20,000 个连接,但应用只能处理 200 个并发请求,用户仍然会等待。
这就是为什么连接调优更改应包括快速的端到端检查。从服务器外部的主机运行一个小型负载测试,观察 Nginx 活跃连接,同时也观察后端。如果后端延迟急剧上升而 Nginx 保持平静,则代理正在履行职责,下一个限制在其后面。
为反向代理工作负载调优
许多 Nginx 服务器充当应用服务器前面的反向代理。在这种角色中,上游行为与 Nginx 容量同样重要。
当 Nginx 重复与同一后端池通信时,使用上游 keepalive:
upstream app_backend {
server 127.0.0.1:3000;
keepalive 32;
}
server {
location / {
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_pass http://app_backend;
}
}
这减少了打开新后端连接的成本。当你的应用接收许多小请求(例如来自仪表板的 API 调用)时,这尤其有用。
同时检查你的超时值。非常长的代理超时可能会在客户端消失或应用程序停止响应后占用工作进程连接。非常短的超时可能会中断合法的慢请求。根据工作负载匹配超时值,而不是在所有地方使用一个默认值。
一个实际场景:你的网站在一天中的大部分时间都很快,但在发送新闻通讯时变慢。CPU 只有 35%,但 Nginx 日志显示连接警告。这指向的不是原始 CPU,而是连接限制、文件描述符或上游排队。提高工作连接数可能会有所帮助,但前提是应用和操作系统能够支持额外的负载。
另一个常见场景是仪表板应用,每个浏览器标签页发出许多小的 API 调用。十个人可能会产生数百个短请求。在这种情况下,上游 keepalive 通常比简单地提高 worker_connections 更重要,因为重复的 TCP 建立到后端变成了不必要的开销。
对于文件下载服务,情况则不同。少量用户可以在下载大文件时长时间保持连接打开。你可能需要足够的工作连接数来处理长时间传输,但你也应该检查 sendfile、磁盘吞吐量、网络吞吐量和客户端超时行为。
对于 WebSocket 或长轮询应用,空闲连接是正常的。高的 Waiting 数量并不自动意味着坏事。问题在于这些空闲连接是否为新请求留下了足够的容量,以及内存使用是否保持可预测。
调优时读取 stub_status
stub_status 模块为你提供了连接行为的快速视图:
Active connections: 291
server accepts handled requests
1162447 1162447 4496426
Reading: 6 Writing: 17 Waiting: 268
Reading 表示 Nginx 正在读取请求头。持续的高值可能指向慢客户端、大头部或攻击模式。Writing 表示 Nginx 正在发送响应。当客户端接收数据缓慢或响应较大时,此值可能会上升。Waiting 表示空闲的 keepalive 连接。在健康的站点上,这个数字可能很高。
accepts 和 handled 计数器通常应该一起移动。如果接受的连接数上升但处理的连接数滞后或出现错误,请检查工作进程限制和文件描述符限制。同时检查内核是否在 Nginx 处理之前丢弃连接。
这些计数器是基本的,但它们很有用,因为它们将连接压力与 CPU 压力分开。如果活跃连接数低而 CPU 高,问题可能不在于 worker_connections。如果活跃连接数高而 CPU 低,则连接限制、keepalive 行为、上游排队或慢客户端更可能是原因。
安全的基线配置
对于小型生产服务器,我宁愿从保守开始并测量:
worker_processes auto;
worker_rlimit_nofile 65535;
events {
worker_connections 4096;
multi_accept on;
}
http {
keepalive_timeout 30s;
keepalive_requests 1000;
}
这不是一个通用的最佳配置。对于许多正常的反向代理工作负载来说,这是一个合理的起点。非常小的虚拟机可能需要更少。繁忙的边缘代理可能需要更多。重要的是 worker_connections 和 worker_rlimit_nofile 对齐。
应用基线后,在类似流量下比较前后指标。不要仅根据重载后幸运的一分钟来判断调优更改。查看 p95 或 p99 延迟、错误率、CPU、内存和后端排队情况,持续足够长的时间以观察真实行为。
使连接调优看起来随机的错误
第一个错误是将请求计数为连接。浏览器可以为一个请求重复使用一个连接,HTTP/2 可以在一个连接上多路复用多个请求。慢客户端也可以在几乎不做有用工作的情况下保持连接打开。这意味着“我们每秒只收到 500 个请求”并不能告诉你 Nginx 需要多少连接。
第二个错误是忘记上游连接。如果 Nginx 提供静态文件,大多数连接是面向客户端的。如果 Nginx 代理到应用,活跃请求通常也需要后端套接字。如果你对上游使用 keepalive,一些后端连接会保持打开以供重用。这很好,但它仍然消耗两端的文件描述符。
第三个错误是在不检查应用程序的情况下提高 Nginx 限制。假设 Nginx 现在可以接受 12,000 个同时连接,但应用程序有 16 个工作进程和一个包含 50 个连接的数据库池。Nginx 将接受比应用能完成的更多工作。用户可能会看到更少的即时连接错误,但延迟可能会变得更糟,因为请求在队列中等待更长时间。
第四个错误是在所有地方使用长的 keepalive 超时。Keepalive 很有用,因为它避免了重复的 TCP 和 TLS 设置。但非常长的超时可能会在流量高峰后留下许多空闲套接字。在内存丰富的边缘代理上,这可能没问题。在小型虚拟机上,它可能会挤占活跃工作。如果你看到巨大的 Waiting 计数和低请求重用,尝试更短的 keepalive_timeout 并再次测量。
故障排除示例
如果错误日志显示 worker_connections are not enough,检查配置值、工作进程数量和进程文件限制:
grep -R "worker_connections\\|worker_processes\\|worker_rlimit_nofile" /etc/nginx/nginx.conf /etc/nginx/conf.d
cat /proc/$(pgrep -o nginx)/limits | grep "open files"
pgrep -o nginx 命令通常找到最旧的 Nginx 进程,通常是主进程。在某些系统上,你可能更喜欢 systemctl status nginx 来查看主 PID。
如果错误日志显示 too many open files,不要只提高 worker_connections。进程正在达到其描述符限制。为 systemd 服务添加或调整 LimitNOFILE,重新加载 systemd,并重启 Nginx,以便实际应用新限制:
sudo systemctl edit nginx
sudo systemctl daemon-reload
sudo systemctl restart nginx
如果用户看到超时但 Nginx 有备用 CPU 且没有连接警告,请查看 Nginx 后面。检查访问日志中的上游响应时间。检查应用工作池。检查数据库连接。反向代理可以顺畅地接受流量,而真正的瓶颈是饱和的后端。
如果流量高峰导致连接重置,但请求未出现在访问日志中,问题可能早于 Nginx 请求处理。查看内核积压设置、负载均衡器日志、防火墙状态表和 SYN 洪水保护。Nginx 无法记录它从未收到的请求。
如何安全测试更改
永远不要通过编辑和希望来调优生产环境。首先测试语法:
sudo nginx -t
然后重新加载 Nginx,以便优雅地处理活跃连接:
sudo systemctl reload nginx
每次更改后监视错误日志:
sudo tail -f /var/log/nginx/error.log
你还应该监控请求延迟、4xx 和 5xx 比率、活跃连接数、CPU、内存和上游响应时间。一个增加连接容量但提高应用延迟的调优更改可能不是真正的胜利。
有关更深入的验证步骤,请参阅 测试 Nginx 配置。
何时请专家
当基本调优后 Nginx 错误仍然存在、流量高峰影响收入,或者你正在生产系统上更改内核网络设置时,请致电经验丰富的 DevOps 工程师或 Web 性能专家。如果你在支付流程、登录系统或关键 API 前调优 Nginx,同样适用。
当瓶颈不明确时,专业帮助也很有用。当真正的问题是慢速数据库、耗尽的上游应用池、过载的 TLS 终止层或负载均衡器超时不匹配时,Nginx 可能看起来是问题所在。
关键要点很简单:调整工作进程以匹配 CPU,调整工作连接数以匹配并发量,并确保 Linux 文件限制支持两者。一次更改一层,在重新加载前测试配置,并测量真实流量,而不是相信理论最大值。