我运行的是一个流量相对较低的网站,该网站在每周更新后都会出现一次访客激增。在此高峰期间,网站性能与本周其他时间相比极差。服务器上的实际负载仍然非常低,可靠地低于 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 左右可能纯粹是巧合。