乐趣区

关于后端:高并发的哲学原理二-Apache-的性能瓶颈与-Nginx-的性能优势

每一名后端开发可能都晓得 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 反对三种过程模型:preforkworkerevent,在此咱们简略讨论一下这三种模式的优缺点。

  1. prefork 是过程模式,须要耗费更多的内存,每次接到一段新的数据,须要应用 select 模型,遍历 TCP 连接数 x 过程数 这么屡次能力找到匹配的过程,在数千个 TCP 连贯下,光是寻找线程就须要消耗掉一个 CPU 外围,单机性能达到极限,无奈利用更多的 CPU 资源
  2. worker 是线程模式,仍旧应用 select 模型来遍历 TCP 申请和线程,性能下限和 prefork 统一,区别是内存消耗量有了一些升高,初始 TCP 承载能力稍好,申请数忽然减少的场景下,开新线程的速度反而比 prefork 更慢,且根底提早比 prefork 模式更高
  3. 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

实验设计

咱们将测试三种配置下的性能体现差别:

  1. Apache 规范模式:prefork + mod_php 插件式运行 PHP
  2. Nginx + php-fpm 专用解释器
  3. Nginx 作为 HTTP 反向代理服务器后接 Apache prefork 模式 + mod_php 插件式运行 PHP

申请打算

  1. 客户端新线程开启后,每隔 5 秒发送一个申请
  2. jmeter 用 50 秒开 5000 个线程,继续压测 100 秒,最大申请 QPS 为 1000

为什么这么设计?

独自比照 Nginx 和 Apache 性能的文章很多,数据后果也大同小异,无非是 Nginx 的 QPS 更高,然而 “为什么是这样?” 却没人答复,我本次的实验设计就是要答复这个问题。

Apache 规范模式:prefork + mod_php

nginx + php-fpm

nginx 反向代理 Apache 规范模式

后果剖析

咱们能够很显著地看出,Apache + prefork 的问题在于它对数千个 TCP 连贯的解决能力有余。

  1. Nginx + fpm 一共收回了 59146 个申请,胜利了五万个
  2. Nginx + Apache 一共收回了 56464 个申请,胜利了五万两千个,比 fpm 还多一些
  3. fpm 模式最大 QPS 为 800 但比较稳定,Nginx + Apache 最大 QPS 1000 但不够稳固
  4. 至于 Apache 规范模式,显然它的技术架构不足以解决 5000 个 TCP 连贯下 1000 QPS 的情况,QPS 低且不稳固,错误率高达 43%

论断:

  1. Nginx 解决海量用户的海量 TCP 连贯的能力超群
  2. 晋升 Apache 性能只须要在后面加一个 Nginx 作为 HTTP 反向代理即可,因为此时 Apache 只须要解决好和 Nginx 之间的大量 TCP 连贯,性能损耗较小
  3. php-fpm 和 mod_php 在执行 PHP 的时候没有任何性能差别

Nginx epoll 和 Apache prefork 模型相比,劣势劣势如下:

劣势
  1. Nginx 每个 worker 过程能够 handle 上千个 TCP 连贯,耗费很少的内存和 CPU 资源,能够让单台服务器承接比 Apache 多两个数量级的用户量,相比于 Apache 单机 5K 的 TCP 连接数下限(对应的是 2000 个在线用户),是一个微小的提高
  2. Nginx 对 TCP 的复用使它十分长于应答海量客户端的间接连贯,依据我的实测,在 HTTP 高并发下 Nginx 的沉闷 TCP 连接数能够做到 Apache 的五分之一,而且用户量越高,复用成果越好
  3. 在架构上,基于 FastCGI 网络协议进行架构扩大,也能够更轻易地利用多台物理服务器的并行计算能力,晋升整个零碎的性能下限
劣势
  1. 在低负载下,Nginx 事件驱动的个性使得每个申请的响应时长比 Apache prefork 模式略长一些(14ms vs 9ms)

我的实在教训

新冠期间的 恋情 时机

疫情初期,我司的私域电商业务衰亡于草莽之间,在 3 月中旬才下班的状况下,半个月的 GMV 超过了后面一年,4 月就实现了全年指标,电商零碎性能压力陡增。

过后咱们的电商零碎是购买的一个 PHP 单体零碎,天生不具备扩展性,外加业务模式是团购秒杀,可要了亲命了。客户端为微信小程序,服务端次要提供两种业务:开团霎时的海量 HTTP API 申请,以及每一个页面都十分耗费资源的订单治理后盾。

过后我面临的第一个问题是数据库顶不住,我找到申请数最高的接口:商品详情,为它减少了一层放弃时长为一分钟的 Redis 缓存,开团霎时数据库的压力升高了很多。

而且侥幸的是过后阿里云刚刚将 PolarDB 商用几个月,我用它顶住了开团三分钟内涌入的大概 4000 名用户,然而当我把虚拟机降级到 16 核 32G 内存的时候呈现了一个十分诡异的景象:

  1. CPU 和内存占用率别离只有 8% 和 6%
  2. 但大量的新用户就是无奈建设 TCP 连贯,首次连贯的客户端体现为长时间的期待
  3. 如果运气好在期待一段时间后胜利进去了,那拜访便会始终如丝般顺滑,每个接口的返回工夫都十分短

这是为什么呢?这是因为新用户无奈和服务器建设 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 多平台公布

退出移动版