Nginx 高并发排障指南:从 worker 饱和到 keepalive 复用

Nginx 高并发排障中的连接分流与资源观察

如何判断 Nginx 响应慢到底是 worker 进程不够、缓冲区配置不合适,还是 keepalive 连接没有复用?很多站长会先想到加 CDN(内容分发网络,通过全球节点缓存内容来缩短访问距离)或升级 [VPS](https://cn.hostease.com/vps/)([虚拟专用服务器](https://cn.hostease.com/vps/),在[物理服务器](https://cn.hostease.com/dedicated-server/)上划分出的独立虚拟环境),但如果 Nginx 本身已经在高并发下排队,外层加速只能缓解一部分症状,无法解决核心瓶颈。

这篇指南的重点不是重复一份通用参数清单,而是教你从日志、压测和资源曲线里定位问题。 你会看到 worker、缓冲区与 keepalive 三类配置分别适合解决什么问题,哪些指标需要先确认,修改后又该用什么命令验证。

先做基线:不要在没有证据时调参数

Nginx(高性能 Web 服务器和反向代理软件)调优最怕“看到配置就改”。同一个 worker_connections 参数,在 2 核 2GB 的博客站、4 核 8GB 的外贸站、前端静态资源网关上的意义完全不同。正确顺序应该是先记录基线,再改一组配置,最后用同一组压测命令复测。

建议先收集 3 类证据:访问日志中的状态码分布、错误日志中的关键报错、系统层面的 CPU(中央处理器)与内存曲线。比如 499 增多常见于客户端等待超时,502/504 更可能指向后端或代理链路,worker_connections are not enough 则直接说明 Nginx 连接上限触顶。关于 TTFB(首字节时间)与主机层优化的关系,可以先参考 TTFB 优化指南,再回到本文做 Nginx 侧排查。

grep 'worker_connections are not enough' /var/log/nginx/error.log | tail -20
awk '{print $9}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head
vmstat 1 10

如果这些命令显示 CPU 长时间低于 40%、内存仍有余量,但请求排队明显,优先看连接数和文件描述符。如果 CPU 已接近 90%,却没有连接上限报错,优先看 gzip、TLS(传输层加密协议)和后端响应。这样做能避免把所有慢请求都归咎于 Nginx 参数。

worker 饱和:先看连接上限,再看进程数量

worker 进程是 Nginx 处理请求的基本执行单元。它采用事件驱动模型,一个 worker 可以同时维护大量连接,但它仍受 CPU 核心、文件描述符和 worker_connections 限制。出现连接排队时,不要只把 worker_processes 改成很大的数字。

worker_processes auto;
worker_rlimit_nofile 65535;

events {
    use epoll;
    worker_connections 2048;
    multi_accept on;
}

worker_processes auto 会按 CPU 核心数启动 worker。对大多数 Linux 服务器来说,这是安全起点。真正经常被忽略的是 worker_rlimit_nofile 与系统 nofile 限制。如果 Nginx 配置里允许 8192 个连接,但系统仍把单进程文件描述符限制在 1024,连接上限会提前触顶。

  • 2 核 2GB 小型站点可以从 worker_connections 1024 起步,总并发约 2048。
  • 4 核 8GB 业务站点可以测试 2048,并同步确认 ulimit -n 不低于 65535。
  • 如果单连接平均占用约 128KB-256KB 内存,把连接数从 4096 提到 16384 前要先算内存余量。
  • 出现 EMFILEtoo many open files 时,先修系统限制,再谈 Nginx 参数。

这里的关键是“连接容量”和“CPU 并行度”分开看。前者由 worker 连接数和文件描述符决定,后者才和 worker 进程数强相关。我们在排查中见过 4 核机器把 worker_processes 设为 32,错误日志没有减少,CPU 上下文切换却明显上升;改回 auto 并提高 nofile 后,连接报错才消失。

Nginx worker 连接容量与系统文件描述符的关系

缓冲区瓶颈:看后端响应大小和磁盘临时文件

如果错误日志没有连接上限报错,但页面在高峰期 TTFB 波动很大,下一步要看缓冲区。Nginx 作为反向代理时,会先读取后端响应,再把数据发送给客户端。缓冲区过小会让大响应频繁写入临时文件;缓冲区过大又会让每个连接占用更多内存。

proxy_buffering on;
proxy_buffer_size 8k;
proxy_buffers 8 16k;
proxy_busy_buffers_size 32k;
proxy_temp_file_write_size 64k;

一个可操作的判断方法是检查错误日志里是否出现 an upstream response is buffered to a temporary file。偶尔出现并不一定是故障,但如果高峰期大量出现,说明后端响应体超过内存缓冲区,Nginx 正在频繁写磁盘。对 WordPress、图片较多的内容页或接口返回较大的站点,这类问题会直接拖慢响应。

调缓冲区时建议按响应大小分层:普通 HTML 页面可从 128KB 总缓冲开始;如果页面包含大量动态片段,可测试 256KB;下载、视频、超大 JSON(轻量数据交换格式)接口不应单纯靠放大代理缓冲解决,而应该优化输出或走专门的静态资源路径。更多站点性能分析思路,可延伸阅读 WordPress 速度优化指南

grep 'buffered to a temporary file' /var/log/nginx/error.log | tail -20
sudo iostat -xz 1 5

如果 iostat 里磁盘 await 长时间升高,同时 Nginx 临时文件日志增多,就说明瓶颈不在 worker 数量,而在响应缓存与磁盘 I/O(输入/输出)。这时盲目增加 worker 只会让更多请求同时写盘,问题可能更明显。

keepalive 失效:高并发下最容易被误判的慢

keepalive 的作用是复用连接,减少重复建立 TCP(传输控制协议)连接的握手成本。对浏览器到 Nginx 的连接如此,对 Nginx 到后端服务的连接也一样。如果 keepalive 没配置好,Nginx 看起来并不忙,后端却会因为短连接洪峰而耗尽连接池。

upstream backend {
    server 127.0.0.1:9000;
    keepalive 32;
    keepalive_requests 1000;
    keepalive_timeout 60s;
}

server {
    location / {
        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    }
}

这里最容易漏的是 proxy_http_version 1.1proxy_set_header Connection ""。只在 upstream 里写 keepalive 32 不够,如果代理请求仍按短连接方式转发,后端连接复用不会真正生效。PHP-FPM、Node.js、Java 应用在高峰时段出现连接数暴涨时,优先检查这一组配置。

客户端侧也要设置合理的空闲时间:

keepalive_timeout 65;
keepalive_requests 100;

对页面资源较多的站点,单个页面加载 60-120 个请求并不少见。keepalive_requests 100 能覆盖大多数页面加载过程,避免每个 CSS、JS 和图片都重新握手。若站点面向跨境访问,单次握手多出的 100ms-300ms 延迟会被放大,keepalive 的收益更明显。若你还在评估服务器网络与应用性能的整体关系,可以结合 服务器配置专题 一起排查。

Nginx keepalive 连接复用减少重复握手

压测验证:每次只改一组配置

修改配置后,验证比配置本身更重要。建议把测试分成“低并发基线”和“接近真实峰值”两组。低并发用于确认配置没有明显错误,高并发用于观察瓶颈是否改善。不要一次改 worker、gzip、buffer、keepalive 四类参数,否则结果变好或变差都无法归因。

nginx -t && systemctl reload nginx
wrk -t4 -c100 -d30s https://example.com/
wrk -t8 -c500 -d60s https://example.com/

复测时至少记录 4 个指标:QPS(每秒查询数)、平均延迟、P95 延迟、失败请求数。比如 QPS 从 3000 提到 4200,但 P95 从 180ms 拉高到 700ms,说明吞吐上去了,尾延迟却变差,线上体验未必更好。配合 htopvmstat 和 Nginx 错误日志一起看,才能判断是不是把压力转移到了 CPU、内存或后端服务。

如果你使用云服务器(基于虚拟化技术提供的弹性计算资源),还要确认实例的 CPU 积分、磁盘 IOPS(每秒输入输出次数)和网络带宽(单位时间内可传输的数据量)是否有平台限制。Nginx 参数只能提升软件层效率,不能突破底层资源上限。Hostease 的相关主机方案适合需要稳定资源和运维支持的业务站点,但具体规格仍应以压测结果和业务峰值为依据。

常见误区:这些改法看起来勤快,实际容易反噬

完成基线和压测后,再回头看很多“调优建议”,你会发现它们只有在特定条件下才成立。下面几类改法尤其需要谨慎:

  • worker_processes 设得远高于 CPU 核心数:容易增加上下文切换,4 核机器设 32 通常没有收益。
  • 无限放大 worker_connections:如果系统文件描述符和内存没有同步调整,只会制造新的限制点。
  • 把 gzip 等级直接设为 9:相比等级 4,压缩率通常只多 2%-5%,CPU 开销却可能明显增加。
  • 只看平均响应时间:平均值会掩盖 P95/P99 尾延迟,线上用户更容易感受到尾部慢请求。
  • 忽略后端连接池:Nginx 正常不代表 PHP-FPM、数据库或应用层没有排队。

更稳妥的做法是把 Nginx 排障当成一条链路:入口连接容量、代理缓冲、后端连接复用、系统资源上限逐段确认。这样即使问题不在 Nginx,也能快速排除它,避免在同一组参数上反复试错。

总结:按证据调优,而不是按模板调优

Nginx 高并发排障的核心建议很简单:先看日志和资源曲线,再改参数;先确认 worker 是否触顶,再检查缓冲区是否写盘,最后验证 keepalive 是否真正复用。每次只修改一组配置,用相同压测命令记录 QPS、P95 延迟和失败请求数,才能判断改动是否有效。

如果你需要给线上站点做一次稳妥的优化,可以按本文顺序建立基线,并把测试结果保存成版本记录。对于访问量增长明显、资源已经接近上限的网站,可以考虑把 Nginx 调优与主机规格评估一起做:先优化配置,确认软件层没有明显浪费,再根据峰值流量选择更合适的 云服务器 或其他托管方案。这样既能避免过早升配,也能减少高峰期临时救火。

发表评论