Apache 性能在超过 256 个并发请求时急剧下降

cmc*_*dry 14 linux performance mpm-prefork apache-2.2

I'm running a relatively low-traffic site that experiences a large spike in visitors once a week following a site update. During this spike, site performance is extremely poor compared to the rest of the week. The actually load on the servers remains very low, reliably under 10% CPU and under 30% RAM (the hardware should be complete overkill for what we're actually doing), but for some reason Apache seems to be unable to cope with the quantity of requests. We are running apache 2.2.3 on RHEL 5.7, kernel 2.6.18-274.7.1.el5, x86_64.

尝试在下班时间使用 ab 重现此行为,我发现当超过大约 256 个用户时,性能会大幅下降。使用我能想到的最小可能用例运行测试(正在检索静态文本文件,总共 223 个字节)性能在 245 个同时请求时始终正常:

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       15   25   5.8     24      37
Processing:    15   65  22.9     76      96
Waiting:       15   64  23.0     76      96
Total:         30   90  27.4    100     125

Percentage of the requests served within a certain time (ms)
  50%    100
  66%    108
  75%    111
  80%    113
  90%    118
  95%    120
  98%    122
  99%    123
 100%    125 (longest request)
Run Code Online (Sandbox Code Playgroud)

但是一旦我同时处理多达 265 个请求,其中的一个子集就开始花费荒谬的时间来完成:

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       13  195 692.6     26    3028
Processing:    15   65  21.3     72     100
Waiting:       15   65  21.3     71      99
Total:         32  260 681.7    101    3058

Percentage of the requests served within a certain time (ms)
  50%    101
  66%    108
  75%    112
  80%    116
  90%    121
  95%   3028
  98%   3040
  99%   3044
 100%   3058 (longest request)
Run Code Online (Sandbox Code Playgroud)

这些结果在多次运行中非常一致。由于还有其他流量流向那个盒子,我不确定硬截断的确切位置(如果有的话),但它似乎可疑地接近 256。

自然地,我认为这是由 prefork 中的线程限制引起的,所以我继续调整配置以将可用线程数量增加一倍,并防止线程池不必要地增长和收缩:

<IfModule prefork.c>
StartServers     512
MinSpareServers  512
MaxSpareServers  512
ServerLimit      512
MaxClients       512
MaxRequestsPerChild  5000
</IfModule>
Run Code Online (Sandbox Code Playgroud)

mod_status 确认我现在正在运行 512 个可用线程

8 requests currently being processed, 504 idle workers
Run Code Online (Sandbox Code Playgroud)

然而,尝试 265 个同时请求仍然产生与之前几乎相同的结果

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       25  211 714.7     31    3034
Processing:    17   94  28.6    103     138
Waiting:       17   93  28.5    103     138
Total:         57  306 700.8    138    3071

Percentage of the requests served within a certain time (ms)
  50%    138
  66%    145
  75%    150
  80%    161
  90%    167
  95%   3066
  98%   3068
  99%   3068
 100%   3071 (longest request)
Run Code Online (Sandbox Code Playgroud)

在搜索文档(和 Stack Exchange)之后,我对进一步的配置设置感到茫然,以试图解决这个瓶颈。有什么我想念的吗?我应该开始寻找 apache 之外的答案吗?有没有其他人看到过这种行为?任何帮助将不胜感激。

编辑:

根据 Ladadadada 的建议,我针对 apache 运行了 strace。我尝试了 -tt 和 -T 几次,但找不到任何不寻常的东西。然后我尝试对所有当前正在运行的 apache 进程运行 strace -c ,并得到这个:

% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 22.09    0.317836           5     62128      4833 open
 19.91    0.286388           4     65374      1896 lstat
 13.06    0.187854           0    407433           pread
 10.70    0.153862           6     27076           semop
  7.88    0.113343           3     38598           poll
  6.86    0.098694           1    100954     14380 read
Run Code Online (Sandbox Code Playgroud)

(……删节)

如果我没看错(请耐心等待,因为我不经常使用 strace)没有任何系统调用可以解释这些请求所花费的时间。看起来瓶颈几乎在请求到达工作线程之前就发生了。

编辑2:

正如几个人所建议的那样,我在 Web 服务器本身上再次运行了测试(以前该测试是从一个中立的互联网位置运行的)。结果令人惊讶:

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0   11   6.6     12      21
Processing:     5  247 971.0     10    4204
Waiting:        3  245 971.3      7    4204
Total:         16  259 973.3     21    4225

Percentage of the requests served within a certain time (ms)
  50%     21
  66%     23
  75%     24
  80%     24
  90%     26
  95%   4225
  98%   4225
  99%   4225
 100%   4225 (longest request)
Run Code Online (Sandbox Code Playgroud)

底线时间与基于互联网的测试相似,但似乎总是更糟在本地运行时。更有趣的是,配置文件发生了巨大变化。而在大量长时间运行的请求的时间花在“连接”上之前,现在瓶颈似乎在于处理或等待。我怀疑这实际上可能是一个单独的问题,以前被网络限制掩盖了。

从与 Apache 主机位于同一本地网络上的另一台机器再次运行测试,我看到了更合理的结果:

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        1    2   0.8      2       4
Processing:    13  118  99.8    205     222
Waiting:       13  118  99.7    204     222
Total:         15  121  99.7    207     225

Percentage of the requests served within a certain time (ms)
  50%    207
  66%    219
  75%    220
  80%    221
  90%    222
  95%    224
  98%    224
  99%    225
 100%    225 (longest request)
Run Code Online (Sandbox Code Playgroud)

这两个测试一起提出了许多问题,但除此之外,现在有一个令人信服的案例来说明在一定负载量下发生的某种严重的网络瓶颈。我认为下一步将分别研究网络层。

Lad*_*ada 4

在这种情况下我会做的是运行

strace -f -p <PID> -tt -T -s 500 -o trace.txt
Run Code Online (Sandbox Code Playgroud)

在 ab 测试期间,在您的 Apache 进程之一上运行,直到捕获缓慢响应之一。那就来看看吧trace.txt

和选项为您提供每个系统调用的开始时间和持续时间的时间戳-tt-T以帮助识别缓慢的系统调用。

您可能会发现一个缓慢的系统调用,例如open()stat(),或者您可能会发现一个快速调用,poll()其后紧接着有(可能是多个)调用。如果您发现正在对文件或网络连接进行操作(很可能),请向后查看跟踪,直到找到该文件或连接句柄。对同一句柄的早期调用应该让您了解正在poll()等待什么。


看看这个-c选项是个好主意。您是否确保您正在跟踪的 Apache 子进程在那段时间至少服务了一个缓慢的请求?(我什至不知道除了strace对所有孩子同时运行之外,你会如何做到这一点。)

不幸的是,strace它并没有让我们全面了解正在运行的程序正在做什么。它只跟踪系统调用。程序内部可以发生很多事情,不需要向内核询问任何事情。要确定是否发生这种情况,您可以查看每个系统调用开始的时间戳。如果你看到明显的差距,那就是时间的流逝。这并不容易被 grep 发现,而且系统调用之间总是存在很小的间隙。

既然您说 CPU 使用率保持在较低水平,那么系统调用之间可能不会发生过多的事情,但值得检查。


更仔细地查看以下输出ab

响应时间的突然跳跃(看起来在 150 毫秒到 3000 毫秒之间没有任何响应时间)表明某个地方发生了特定的超时,该超时在大约 256 个并发连接上被触发。如果正常 IO 耗尽 RAM 或 CPU 周期,则预计会出现更平滑的降级。

其次,ab响应慢说明该connect阶段花费了3000ms。几乎所有的耗时都在 30 毫秒左右,但 5% 的耗时为 3000 毫秒。这表明网络是问题所在。

你从哪里跑ab?您可以在与 Apache 机器相同的网络上尝试一下吗?

tcpdump要获取更多数据,请尝试在连接的两端运行(最好ntp在两端运行,以便可以同步两个捕获。)并查找任何 tcp 重新传输。Wireshark 特别适合分析转储,因为它以不同的颜色突出显示 TCP 重传,使它们易于查找。

还可能值得查看您有权访问的任何网络设备的日志。我最近遇到了我们的一个防火墙的问题,它可以处理以 kb/s 为单位的带宽,但无法处理每秒接收的数据包数量。最高可达每秒 140,000 个数据包。对您的运行进行一些快速计算后,ab我相信您每秒会看到大约 13,000 个数据包(忽略 5% 的缓慢请求)。也许这就是你已经达到的瓶颈。这发生在 256 年左右的事实可能纯粹是巧合。