上一篇 下一篇 分享链接 返回 返回顶部

Apache与Nginx的工作原理有何不同?连接处理与性能如何验证

发布人:Minchunlin 发布时间:2026-10-03 21:23 阅读量:4

Apache 和 Nginx 的区别,不能简单归结为“一个慢、一个快”。Nginx 通常以事件驱动模型处理大量并发连接,Apache 则通过 MPM(多处理模块)在进程、线程或事件模型之间切换;但现代 Apache 的 event MPM 同样可以高效处理 Keep-Alive 连接。因此,静态文件、高并发空闲连接和动态应用请求,可能得出完全不同的性能结果。

要回答 Apache 与 Nginx 的工作原理有何不同,建议先检查实际启用的进程模型,再在相同内容、相同协议、相同并发和相同日志策略下压测。重点观察每秒请求数、P95/P99 延迟、错误率、CPU、内存、文件描述符和后端耗时,而不是只看某一次测试输出的吞吐量。

先明确“连接处理”与“请求处理”的区别

一个 TCP 连接不一定只承载一个 HTTP 请求。HTTP/1.1 Keep-Alive 会让同一连接连续发送多个请求;反向代理场景中,一个客户端连接还可能对应一个到后端的连接。因此:

  • 并发连接数不等于每秒请求数。
  • worker_connections 不等于 Nginx 能处理的 HTTP 请求数。
  • Apache 的 MaxRequestWorkers 更接近同时处理请求的能力,而不是简单的 TCP 连接总数。
  • 高并发连接占用的主要是文件描述符、连接状态和少量内存;真正执行动态代码时,还会消耗线程、进程和 CPU。
对比项ApacheNginx
核心结构由 MPM 决定,可使用 prefork、worker 或 eventMaster-Worker 加事件循环
连接等待取决于 MPM;旧式进程模型可能让连接长期占用进程Worker 使用非阻塞事件机制管理多个连接
静态文件通过模块和进程/线程处理事件循环配合文件 I/O、缓存或 sendfile
动态请求可通过模块、CGI、代理或应用服务器执行通常转发给 PHP-FPM、Java、Python 等后端
配置方式模块丰富,也可支持目录级配置以中心化配置为主,不使用 Apache 的 .htaccess
典型优势兼容性、模块生态和目录级控制较灵活高并发连接、静态资源和反向代理场景资源开销较可控
典型限制进程、线程和模块会增加资源消耗动态执行依赖后端,复杂模块或阻塞操作仍可能拖慢 Worker

Apache 如何处理连接

Apache 的实际行为首先取决于启用的 MPM。可以把它理解为同一套 Web 服务框架下的不同调度方式。

prefork:进程隔离明显,资源开销较高

prefork 模式通常由多个子进程处理请求,每个子进程同一时间处理一个请求。早期依赖进程内模块的应用常使用这种方式,因为模块不需要考虑多线程安全问题。

它的特点是隔离性较直观:一个子进程异常,通常不会直接影响其他子进程。但每个进程都需要独立的地址空间和运行环境,在并发连接数增加时,内存占用可能快速上升。

如果大量连接处于 Keep-Alive 等待状态,进程模型可能出现“连接没有进行计算,却占用了处理资源”的情况。具体行为还与 Apache 版本、配置和 MPM 实现有关,不能仅根据 Apache 软件名称判断。

worker:一个进程包含多个线程

worker 模式由多个子进程和多个线程共同工作。线程共享进程内存,通常比一连接一进程的模式节省内存,适合多线程安全的模块和应用。

但线程共享资源也增加了并发安全、锁竞争和模块兼容方面的要求。如果某个模块不是线程安全的,或者请求执行过程中存在阻塞操作,线程模型的优势可能被削弱。

event:将空闲连接与活跃请求分开处理

event MPM 进一步优化了 Keep-Alive 连接的处理方式。监听线程可以管理处于等待状态的连接,把真正需要读取、解析或发送数据的请求交给工作线程处理。

这意味着 Apache 并非永远是“一个连接对应一个进程”。在启用 event MPM 后,Apache 与 Nginx 在连接等待阶段的差距可能明显缩小。需要注意的是,具体模块是否支持事件模型、请求是否会回退到传统处理方式,仍取决于实际版本和模块组合。

查看 Apache 当前使用的 MPM,不要只看配置文件名称,应以运行结果为准:

apachectl -V
apachectl -M | grep -i mpm

在部分发行版中命令可能是 apache2ctl,服务名也可能是 httpd 而不是 apache2。如果命令没有输出,先确认 Apache 的管理命令和安装路径。

Nginx 如何处理连接

Nginx 通常由一个 Master 进程和多个 Worker 进程组成:

Nginx 如何处理连接配图

  1. Master 读取配置、创建监听套接字并管理 Worker。
  2. Worker 通过 epoll、kqueue 或系统对应的事件机制监听多个连接。
  3. 当某个连接可读或可写时,Worker 才处理对应事件。
  4. 静态请求直接读取文件并返回。
  5. 动态请求或反向代理请求通过非阻塞 I/O 与上游服务交换数据。

Nginx Worker 并不是为每个连接创建一个线程,而是使用事件循环轮询大量套接字。大量连接处于等待状态时,每个连接只保留必要的状态,不需要长期占用独立进程或线程。

但“事件驱动”不等于“任何请求都不会阻塞”。以下操作仍可能阻塞 Worker 或显著增加 CPU 消耗:

  • 执行耗时的第三方模块;
  • 大量压缩、加密或复杂正则匹配;
  • 慢速磁盘读取;
  • 不合理的日志写入;
  • 上游响应迟迟不返回;
  • 在 Worker 中执行同步、耗时的业务逻辑。

Nginx 中常见的 worker_processes 和 worker_connections 也不能脱离系统限制理解。理论上的连接数量可近似表示为:

Worker 数量 × 每个 Worker 的 worker_connections

但实际可用上限还会受到文件描述符、监听队列、内存、内核参数以及反向代理上游连接数影响。一个客户端连接加一个上游连接时,连接资源消耗可能接近两个套接字,而不是一个。

查看 Nginx 编译参数和最终生效配置:

nginx -V 2>&1
nginx -T 2>&1 | sed -n '1,180p'

nginx -T 会输出完整配置,适合确认 worker_processes、worker_connections、Keep-Alive、代理缓冲和日志配置是否真的生效。

为什么静态请求和动态请求会得出不同结果

性能差异通常来自四个层面。

1. 连接等待成本

如果测试是大量长连接、低请求频率,主要考察的是连接状态管理、文件描述符和事件调度。此时 Nginx 的事件模型通常更容易保持较低的内存开销,但启用 Apache event MPM 后,结果可能接近。

2. 请求处理成本

如果每个请求都需要复杂规则、认证、压缩、日志或模块处理,单纯比较连接模型就不够了。Apache 的模块链、目录级配置检查和请求钩子可能增加处理步骤;Nginx 则可能因第三方模块或复杂配置消耗更多 CPU。

3. 后端应用成本

动态请求通常经过 PHP-FPM、Java、Python 或其他应用服务。此时 Web 服务器只负责接收请求、转发数据和返回结果。如果后端 CPU 已经达到瓶颈,换 Apache 或 Nginx 对吞吐量的影响可能很小。

4. 网络与协议成本

TLS 握手、HTTP/2 多路复用、响应压缩、客户端网络延迟和丢包都会改变结果。使用本机回环地址测试,主要验证 Web 服务自身;使用远端压测机,才会把网络路径和真实传输过程纳入结果。

因此,测试静态小文件时,可能看到明显的连接处理差异;测试一个平均耗时 80 毫秒的动态接口时,两者结果可能都被后端耗时主导。

一套可复现的性能验证方法

下面以 Linux 主机、HTTP/1.1、静态文件和可选动态接口为例。示例命令默认访问 127.0.0.1,只适用于自有主机或已获授权的测试环境,不应直接对生产服务进行高并发压测。

1. 记录测试环境

至少记录以下内容:

  • CPU 逻辑核心数和内存;
  • Linux 发行版、内核版本;
  • Apache 或 Nginx 的版本;
  • Apache 当前 MPM;
  • 静态文件大小和内容;
  • 是否开启 Keep-Alive、TLS、压缩和访问日志;
  • 测试工具版本;
  • 压测端与服务端是否为同一台机器;
  • 每轮测试的持续时间、并发连接数和重复次数。

可以先执行基础检查:

uname -a
nproc
free -h
nginx -v
apachectl -v
ulimit -n

如果 Apache 使用的是 apache2ctl,将命令替换为对应名称。服务通过 systemd 管理时,还可以检查服务级文件描述符限制:

systemctl show nginx -p LimitNOFILE
systemctl show apache2 -p LimitNOFILE

2. 确认请求确实到达目标服务

Apache 和 Nginx 应使用相同的测试文件、相同的响应头策略和相同的协议。先用 curl 验证状态码和延迟:

curl --http1.1 -fsS -o /dev/null \
  -w 'code=%{http_code} connect=%{time_connect}s ttfb=%{time_starttransfer}s total=%{time_total}s\n' \
  http://127.0.0.1:8080/test-1k.txt

连续执行几次,观察是否稳定返回 200。如果两个服务器返回的内容不一致,可以分别保存响应并比较摘要:

curl --http1.1 -fsS http://127.0.0.1:8080/test-1k.txt -o /tmp/web-test-a.bin
sha256sum /tmp/web-test-a.bin

测试文件应放在专用目录,文件名应避免覆盖现有业务文件。静态测试至少准备一个较小文件和一个较大文件,否则只能观察单一 I/O 场景。

3. 先做低并发基线,再提高并发

可以使用 wrk 进行持续压测。以下数据只是测试流程示例,具体并发应根据文件描述符和机器资源逐步增加:

wrk -t2 -c10 -d30s --latency \
  http://127.0.0.1:8080/test-1k.txt

预热完成后,使用相同参数测试多个并发档位:

for c in 1 10 50 100 300
do
  echo "===== connections: $c ====="
  wrk -t4 -c"$c" -d60s --latency \
    http://127.0.0.1:8080/test-1k.txt
done

每个档位至少重复三次,记录平均值和波动范围。不要一次只跑一个高并发结果,因为首次加载文件、连接建立、CPU 频率变化和后台任务都可能影响输出。

Keep-Alive 应单独作为变量进行测试。默认 Keep-Alive 与关闭连接复用的结果不能混在同一张表中:

wrk -t4 -c100 -d60s --latency \
  -H 'Connection: close' \
  http://127.0.0.1:8080/test-1k.txt

如果比较动态接口,应保证接口返回内容稳定,并尽量避免数据库写入、随机计算和外部网络调用。静态接口与动态接口的结果应分开记录,不能用静态文件的吞吐量推断业务接口容量。

4. 同时观察系统资源

仅记录 wrk 输出不够。压测期间可以在另一个终端观察系统状态:

vmstat 1

查看 Nginx 进程的 CPU 和内存:

pidstat -dur -C nginx 1

查看 Apache 进程:

pidstat -dur -C apache2 1

如果发行版中的进程名不同,先用以下命令确认:

pgrep -a nginx
pgrep -a apache2
pgrep -a httpd

没有 pidstat 输出时,可以改用具体 PID:

pidstat -dur -p 1234,1235 1

连接和监听状态可以通过以下命令辅助判断:

ss -s
ss -lntp | grep -E ':(8080|8081)\b'

重点关注以下指标:

指标观察重点常见解释
Requests/sec吞吐量是否随并发增加到达平台后说明某个资源接近上限
P50常规请求体验反映大多数请求的延迟
P95/P99尾部延迟适合发现排队、慢后端和资源争用
Socket errors连接或读写失败可能与监听队列、文件描述符或客户端中断有关
CPU user/system用户态和内核态消耗区分应用计算与网络、系统调用开销
RSS进程常驻内存Apache 多进程或模块加载可能使总量上升
Context switches上下文切换数量线程、进程过多或竞争激烈时可能增加
Established 连接数实际连接规模验证并发参数是否真正生效

如何解读一组示例结果

下面是一组模拟数据,仅用于说明判断方法,不代表特定机器、版本或实际测量结论。测试条件设为:同一台 Linux 主机、1 KiB 静态文件、HTTP/1.1 Keep-Alive、100 个并发连接、单轮 60 秒,内存为所有相关进程的合计值。

如何解读一组示例结果配图

服务模型吞吐量P95 延迟错误率总 RSS
Apache prefork约 4,300 req/s34 ms0约 460 MB
Apache event约 17,200 req/s13 ms0约 145 MB
Nginx Worker约 19,400 req/s11 ms0约 82 MB

这组数据可以说明三个现象:

  1. Apache 的差异可能来自 MPM,而不是软件名称本身。将 prefork 与 event 混为一个“Apache 性能值”会造成误判。
  2. Nginx 与 Apache event 的差距已经比 Nginx 与 Apache prefork 的差距小,说明连接处理模型比品牌标签更重要。
  3. RSS 较低不代表所有请求都更快,还需要结合 CPU、尾部延迟和错误率判断。

如果把测试对象换成一个后端平均耗时较高的动态接口,可能得到类似结果:

服务模型吞吐量P95 延迟后端 CPU
Apache event约 4,850 req/s84 ms96%
Nginx Worker约 4,900 req/s82 ms96%

此时两者的吞吐量接近,原因不是连接模型没有差异,而是后端已经成为主要瓶颈。继续调大 worker_connections 或 Apache 工作线程,通常不能线性提高整体吞吐量,反而可能增加排队和超时风险。

常见测试异常及其含义

吞吐量随并发增加后不再上升

先看 CPU:

  • CPU 接近满载,说明请求处理、压缩、TLS 或应用计算可能已经饱和;
  • CPU 不高但延迟持续上升,可能是磁盘、锁、后端连接池或监听队列受限;
  • Nginx 反向代理场景还要统计上游连接,因为客户端连接数增加不一定等于后端处理能力增加。

出现“Too many open files”

这通常意味着进程级或服务级文件描述符不足。Nginx 的 worker_connections 即使设置得很高,也不能突破进程的 LimitNOFILE;Apache 也会受到系统限制。

不要直接把参数改到很大。先记录当前值、确认内存和连接规模,再逐步调整,并在调整后重新运行相同并发档位。

Apache 返回 503 或请求排队

需要检查当前 MPM 的工作线程、进程数和 MaxRequestWorkers 等配置。503 可能表示可用工作槽位耗尽,也可能来自后端不可用,不能只根据状态码判断原因。

Nginx 返回 502 或 504

这通常优先指向上游服务、上游连接、应用响应时间或代理超时配置,而不是 Nginx 的静态连接模型。应同时查看 Nginx 错误日志、后端监听端口和后端应用日志。

压测结果波动很大

常见原因包括:

  • 压测端与服务端共用 CPU;
  • 测试文件首次读取触发磁盘缓存;
  • 一次开启日志、一次关闭日志;
  • 一次使用 Keep-Alive,另一次使用短连接;
  • 测试期间有备份、更新或其他后台任务;
  • 每轮测试的并发、线程数或持续时间不同。

应先固定变量,再重复三轮以上。若要观察真实用户延迟,不能只使用回环地址;若只验证 Web 服务连接处理能力,回环测试反而能减少网络因素干扰。

修改配置后的复测边界

性能测试前不一定需要调参。应先确认当前配置,再建立基线。如果确实需要修改配置,先备份对应文件,记录变更内容和影响范围。Nginx 可以先做语法检查:

sudo cp /etc/nginx/nginx.conf \
  /etc/nginx/nginx.conf.bak.$(date +%F-%H%M%S)

sudo nginx -t

检查通过后再按实际服务管理方式重新加载。Apache 同样先检查语法:

sudo apachectl configtest

不同发行版的配置文件路径、服务名和包含文件可能不同,不能只恢复主配置文件而忽略 conf.d、站点配置或模块配置。回滚时恢复备份,重新执行语法检查,再进行 reload;如果服务已处于异常状态,应避免在没有确认影响范围的情况下反复重启。

每次复测至少保持以下条件不变:

  • 相同的服务版本和编译模块;
  • 相同的静态文件或动态接口;
  • 相同的 HTTP 协议、Keep-Alive 和 TLS 设置;
  • 相同的日志、压缩和缓存策略;
  • 相同的压测工具、线程数、并发数和持续时间;
  • 相同的预热和重复次数。

结果的适用边界

如果目标是大量静态文件、反向代理和长连接,Nginx 的事件驱动模型通常值得优先验证;如果依赖丰富模块、目录级配置、既有 Apache 规则或特定应用兼容性,Apache 的 MPM 选择和模块组合更关键。

但不能从一次静态文件压测直接得出动态业务结论,也不能从回环地址测试推断远程用户的网络延迟。真正有参考价值的结果,应同时说明连接模型、测试内容、协议、并发、资源使用和后端状态。

判断 Apache 与 Nginx 谁更适合当前服务,最可靠的方式不是比较软件名称,而是先确认实际工作模型,再用低并发基线、高并发曲线、尾部延迟和错误率进行复测。只有当吞吐量、P95/P99、资源消耗和应用后端状态共同指向同一个瓶颈时,测试结果才足以支持配置或架构调整。

目录结构
全文