每一名后端开发可能都晓得 Nginx 比 Apache 性能强,然而为什么强,强在哪里,接下来咱们入手试验解答这个问题。
Nginx 利用了新的 Linux kernel API
Nginx 利用了 Linux 内核引入的 epoll 事件驱动 API,大幅升高了海量 TCP 连贯下的 CPU 负载,晋升了单个零碎的 TCP 响应容量,这是 Nginx 性能更好的实质起因:时代在提高。
每一篇技术文章都会说,Nginx 的 epoll 比 Apache 的 select 性能高,但为什么,却简直没人解释。上面我来尝试解释。
epoll 简略解释
家喻户晓,epoll 是一种高性能事件驱动 IO 多路复用机制,那他和 select 这种原始 IO 多路复用机制比有什么劣势呢?简略来说,就是:转守为攻。
epoll 化被动为被动,以前须要两次遍历能力实现的网络数据包和线程的匹配,当初通过事件驱动的形式被动献上指针,性能暴增。这就像云原生时代的 Prometheus 监控:化被动上传为被动查问,大幅晋升单个采集节点的性能下限,胜利解决了监控畛域的高并发性能问题。
在 5K 个 TCP 连贯的状况下,每收到一个数据包,Nginx 找到对应线程的速度比 Apache 高了两个数量级,即使是 event 模式下的 Apache,性能仍然远低于 Nginx,因为 Nginx 就是专门为“反向代理”设计的,而 Apache 实质是个 web 利用容器,无奈做到纯正的事件驱动,性能天然无奈和 Nginx 相比。
Apache 的原始并发模型
Apache 反对三种过程模型:prefork
、worker
和 event
,在此咱们简略讨论一下这三种模式的优缺点。
- prefork 是过程模式,须要耗费更多的内存,每次接到一段新的数据,须要应用
select
模型,遍历TCP 连接数 x 过程数
这么屡次能力找到匹配的过程,在数千个 TCP 连贯下,光是寻找线程就须要消耗掉一个 CPU 外围,单机性能达到极限,无奈利用更多的 CPU 资源 - worker 是线程模式,仍旧应用
select
模型来遍历 TCP 申请和线程,性能下限和 prefork 统一,区别是内存消耗量有了一些升高,初始 TCP 承载能力稍好,申请数忽然减少的场景下,开新线程的速度反而比 prefork 更慢,且根底提早比 prefork 模式更高 - event 模式采纳和 Nginx 统一的
epoll
模型承载,实践上体现和 Nginx 统一,但因为 Apache 大概率是和 mod_php(插件)模式的 PHP 一起部署,再加上 PHP 阻塞运行的个性,性能和下面两兄弟并无显著区别。因而即使是 event 模式下的 Apache,性能仍然远低于 Nginx。
接下来咱们应用 jmeter 测试一下 prefork、worker、event 三种模式的性能。
压力测试
测试环境
-
客户端:
- i5-10400 6 核 12 线程
- 32GB 内存
- 千兆有线网络
-
软件环境
- macOS
- Java 19.0.1
-
服务端:
- 物理服务器 E5-2682V4 2.5GHz 16 核 32 线程 * 2(阿里云 5 代 ECS 同款 CPU)256GB RAM
- 虚拟机 64 vCPU(赋予了虚拟机所有母机的 CPU 资源)
- 虚拟机内存 32GB
-
软件环境
- CentOS Stream release 9
- kernel 5.14.0-200.el9.x86_64
- Apache/2.4.53
- Nginx/1.20.1
- PHP 8.0.26
-
PHP 环境:
- Laravel 9.19
- 给默认路由减少 sleep 500ms 的代码,模仿数据库、Redis、RPC、cURL 微服务等场景
- 执行
php artisan optimize
后测试
- 测试代码
Route::get('/', function () {usleep(500000);
return view('welcome');
});
- Apache prefork 模式配置
<IfModule mpm_prefork_module>
StartServers 100
MinSpareServers 5
MaxSpareServers 100
MaxRequestWorkers 500
MaxRequestsPerChild 100000
</IfModule>
- php-fpm 配置
pm = static
pm.max_children = 500
实验设计
咱们将测试三种配置下的性能体现差别:
- Apache 规范模式:prefork + mod_php 插件式运行 PHP
- Nginx + php-fpm 专用解释器
- Nginx 作为 HTTP 反向代理服务器后接 Apache prefork 模式 + mod_php 插件式运行 PHP
申请打算
- 客户端新线程开启后,每隔 5 秒发送一个申请
- jmeter 用 50 秒开 5000 个线程,继续压测 100 秒,最大申请 QPS 为 1000
为什么这么设计?
独自比照 Nginx 和 Apache 性能的文章很多,数据后果也大同小异,无非是 Nginx 的 QPS 更高,然而 “为什么是这样?” 却没人答复,我本次的实验设计就是要答复这个问题。
Apache 规范模式:prefork + mod_php
nginx + php-fpm
nginx 反向代理 Apache 规范模式
后果剖析
咱们能够很显著地看出,Apache + prefork 的问题在于它对数千个 TCP 连贯的解决能力有余。
- Nginx + fpm 一共收回了 59146 个申请,胜利了五万个
- Nginx + Apache 一共收回了 56464 个申请,胜利了五万两千个,比 fpm 还多一些
- fpm 模式最大 QPS 为 800 但比较稳定,Nginx + Apache 最大 QPS 1000 但不够稳固
- 至于 Apache 规范模式,显然它的技术架构不足以解决 5000 个 TCP 连贯下 1000 QPS 的情况,QPS 低且不稳固,错误率高达 43%
论断:
- Nginx 解决海量用户的海量 TCP 连贯的能力超群
- 晋升 Apache 性能只须要在后面加一个 Nginx 作为 HTTP 反向代理即可,因为此时 Apache 只须要解决好和 Nginx 之间的大量 TCP 连贯,性能损耗较小
- php-fpm 和 mod_php 在执行 PHP 的时候没有任何性能差别
Nginx epoll 和 Apache prefork 模型相比,劣势劣势如下:
劣势
- Nginx 每个 worker 过程能够 handle 上千个 TCP 连贯,耗费很少的内存和 CPU 资源,能够让单台服务器承接比 Apache 多两个数量级的用户量,相比于 Apache 单机 5K 的 TCP 连接数下限(对应的是 2000 个在线用户),是一个微小的提高
- Nginx 对 TCP 的复用使它十分长于应答海量客户端的间接连贯,依据我的实测,在 HTTP 高并发下 Nginx 的沉闷 TCP 连接数能够做到 Apache 的五分之一,而且用户量越高,复用成果越好
- 在架构上,基于 FastCGI 网络协议进行架构扩大,也能够更轻易地利用多台物理服务器的并行计算能力,晋升整个零碎的性能下限
劣势
- 在低负载下,Nginx 事件驱动的个性使得每个申请的响应时长比 Apache prefork 模式略长一些(14ms vs 9ms)
我的实在教训
新冠期间的 恋情 时机
疫情初期,我司的私域电商业务衰亡于草莽之间,在 3 月中旬才下班的状况下,半个月的 GMV 超过了后面一年,4 月就实现了全年指标,电商零碎性能压力陡增。
过后咱们的电商零碎是购买的一个 PHP 单体零碎,天生不具备扩展性,外加业务模式是团购秒杀,可要了亲命了。客户端为微信小程序,服务端次要提供两种业务:开团霎时的海量 HTTP API 申请,以及每一个页面都十分耗费资源的订单治理后盾。
过后我面临的第一个问题是数据库顶不住,我找到申请数最高的接口:商品详情,为它减少了一层放弃时长为一分钟的 Redis 缓存,开团霎时数据库的压力升高了很多。
而且侥幸的是过后阿里云刚刚将 PolarDB 商用几个月,我用它顶住了开团三分钟内涌入的大概 4000 名用户,然而当我把虚拟机降级到 16 核 32G 内存的时候呈现了一个十分诡异的景象:
- CPU 和内存占用率别离只有 8% 和 6%
- 但大量的新用户就是无奈建设 TCP 连贯,首次连贯的客户端体现为长时间的期待
- 如果运气好在期待一段时间后胜利进去了,那拜访便会始终如丝般顺滑,每个接口的返回工夫都十分短
这是为什么呢?这是因为新用户无奈和服务器建设 TCP 连贯!
默认状况下,CentOS 7.9 的最大文件关上数(ulimit)为 1024,所有皆文件,每个 TCP 连贯也是一个文件,所以也被锁定在了 1024 个。个别大家都会把这个数字设置为 65535,然而我察看到,这台虚拟机此时的 TCP 连接数只能跑到 5-6K 之间,远远达不到用户的需要,无论是采纳 prefork、worker 还是 event 都是这样。而起因就是咱们下面实测过的:此时 Apache 破费了一颗外围的全副工夫片来进行数据包和线程的匹配,曾经忙不过来了。
起初,我在这台机器上装置了一个 Nginx,反向代理全副的用户申请再发送给 Apache:申请一下子舒畅了,而且 Nginx 应用的最大沉闷 TCP 连贯数量也只有 1K,就齐全满足了三分钟 4000 用户的需要。
还记得咱们的指标吗?一百万 QPS
在 2022 年的支流云服务器硬件上,通过 OPCache 性能优化的 PHP 利用,只须要 2 vCore 便能够达到 5K 的单机 Apache TCP 下限,此时 QPS 在 200 左右,单纯晋升外围数量无奈让这个数字大幅减少。而通过应用 Nginx,咱们能够将单零碎的 QPS 下限从 200 晋升到 1000。
出自:https://github.com/johnlui/PPHC
本文由 mdnice 多平台公布