当同时请求数超过约 256 个时,Apache 的性能会急剧下降

当同时请求数超过约 256 个时,Apache 的性能会急剧下降

我运行的是一个流量相对较低的网站,该网站在每周更新后都会出现一次访客激增。在此高峰期间,网站性能与本周其他时间相比极差。服务器上的实际负载仍然非常低,可靠地低于 10% CPU 和 30% RAM(对于我们实际执行的操作,硬件应该完全过载),但出于某种原因,Apache 似乎无法应对大量请求。我们在 RHEL 5.7、内核 2.6.18-274.7.1.el5、x86_64 上运行 apache 2.2.3。

尝试在非工作时间使用 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)

但是,当我将同时处理的请求数增加到 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)

这些结果在多次运行中非常一致。由于还有其他流量进入该框,我不确定硬截止点的确切位置(如果有的话),但它似乎非常接近 256。

自然而然地,我认为这是由 prefork 中的线程限制引起的,因此我继续调整配置,使可用线程数加倍,并防止线程池不必要地增大和缩小:

<IfModule prefork.c>
StartServers     512
MinSpareServers  512
MaxSpareServers  512
ServerLimit      512
MaxClients       512
MaxRequestsPerChild  5000
</IfModule>

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

8 requests currently being processed, 504 idle workers

然而,尝试同时进行 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)

在仔细阅读文档(和 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

(……节选)

如果我没看错的话(请耐心听我说,因为我不经常使用 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)

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

从与 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)

这两项测试一起提出了许多问题,但除此之外,现在有令人信服的案例表明,在一定负载下会发生某种严重的网络瓶颈。我认为下一步将单独调查网络层。

答案1

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

strace -f -p <PID> -tt -T -s 500 -o trace.txt

在 ab 测试期间,检查某个 Apache 进程,直到捕获其中一个响应缓慢。然后查看trace.txt

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

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


看看这个-c选项,这是一个好主意。您是否确保您跟踪的 Apache 子进程在这段时间内至少处理了一个缓慢的请求?(我甚至不确定除了strace同时在所有子进程上运行之外,您还能如何做到这一点。)

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

既然你说 CPU 使用率保持较低,那么大概系统调用之间没有发生过多的事情,但值得检查。


仔细查看输出ab

响应时间的突然增加(似乎在 150 毫秒到 3000 毫秒之间没有响应时间)表明在某个地方发生了特定的超时,该超时在超过 256 个同时连接时触发。如果内存或 CPU 周期正常 IO 不足,则预计降级会更平稳。

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

你从哪里运行ab?你能从与 Apache 机器相同的网络尝试吗?

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

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

相关内容