我想了解一些我遇到的(对我们来说)负载过重的 Web 服务器的性能问题。环境如下:
- Debian Lenny(所有稳定软件包 + 修补安全更新)
- Apache 2.2.9
- PHP 5.2.6
- Amazon EC2 大型实例
我们看到的行为是,Web 通常感觉响应迅速,但开始处理请求时会稍有延迟 —— 有时是几分之一秒,有时在高峰使用时间是 2-3 秒。服务器上的实际负载被报告为非常高 —— 通常为 10.xx 或 20.xx(如 报告的那样)top
。此外,在这些时间(甚至 )在服务器上运行其他程序vi
非常慢,因此负载肯定很高。奇怪的是,除了最初的延迟之外,Apache 仍然非常灵敏。
我们使用 prefork 对 Apache 进行如下配置:
StartServers 5
MinSpareServers 5
MaxSpareServers 10
MaxClients 150
MaxRequestsPerChild 0
并将 KeepAlive 设置为:
KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 5
查看服务器状态页面,即使在负载很重的时候,我们也很少达到客户端上限,通常处理 80-100 个请求,其中许多请求处于保持活动状态。这告诉我可以将初始请求缓慢排除为“等待处理程序”,但我可能错了。
亚马逊的 CloudWatch 监控告诉我,即使我们的操作系统报告负载 > 15,我们的实例 CPU 利用率也在 75-80% 之间。
示例输出来自top
:
top - 15:47:06 up 31 days, 1:38, 8 users, load average: 11.46, 7.10, 6.56
Tasks: 221 total, 28 running, 193 sleeping, 0 stopped, 0 zombie
Cpu(s): 66.9%us, 22.1%sy, 0.0%ni, 2.6%id, 3.1%wa, 0.0%hi, 0.7%si, 4.5%st
Mem: 7871900k total, 7850624k used, 21276k free, 68728k buffers
Swap: 0k total, 0k used, 0k free, 3750664k cached
大多数流程如下:
24720 www-data 15 0 202m 26m 4412 S 9 0.3 0:02.97 apache2
24530 www-data 15 0 212m 35m 4544 S 7 0.5 0:03.05 apache2
24846 www-data 15 0 209m 33m 4420 S 7 0.4 0:01.03 apache2
24083 www-data 15 0 211m 35m 4484 S 7 0.5 0:07.14 apache2
24615 www-data 15 0 212m 35m 4404 S 7 0.5 0:02.89 apache2
vmstat
与上述同时的输出示例:
procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
r b swpd free buff cache si so bi bo in cs us sy id wa
8 0 0 215084 68908 3774864 0 0 154 228 5 7 32 12 42 9
6 21 0 198948 68936 3775740 0 0 676 2363 4022 1047 56 16 9 15
23 0 0 169460 68936 3776356 0 0 432 1372 3762 835 76 21 0 0
23 1 0 140412 68936 3776648 0 0 280 0 3157 827 70 25 0 0
20 1 0 115892 68936 3776792 0 0 188 8 2802 532 68 24 0 0
6 1 0 133368 68936 3777780 0 0 752 71 3501 878 67 29 0 1
0 1 0 146656 68944 3778064 0 0 308 2052 3312 850 38 17 19 24
2 0 0 202104 68952 3778140 0 0 28 90 2617 700 44 13 33 5
9 0 0 188960 68956 3778200 0 0 8 0 2226 475 59 17 6 2
3 0 0 166364 68956 3778252 0 0 0 21 2288 386 65 19 1 0
最后,Apache 的输出server-status
:
Server uptime: 31 days 2 hours 18 minutes 31 seconds
Total accesses: 60102946 - Total Traffic: 974.5 GB
CPU Usage: u209.62 s75.19 cu0 cs0 - .0106% CPU load
22.4 requests/sec - 380.3 kB/second - 17.0 kB/request
107 requests currently being processed, 6 idle workers
C.KKKW..KWWKKWKW.KKKCKK..KKK.KKKK.KK._WK.K.K.KKKKK.K.R.KK..C.C.K
K.C.K..WK_K..KKW_CK.WK..W.KKKWKCKCKW.W_KKKKK.KKWKKKW._KKK.CKK...
KK_KWKKKWKCKCWKK.KKKCK..........................................
................................................................
根据我有限的经验,我得出以下结论/问题:
我们可能允许了太多
KeepAlive
请求我确实看到 vmstat 中等待 IO 的时间有些长,虽然不是一直有,也不是很多(我想是吧?),所以我不确定这是否是一个大问题,我对 vmstat 不太熟悉
此外,在 vmstat 中,我在一些迭代中看到许多进程正在等待服务,这就是我认为我们的 Web 服务器上的初始页面加载延迟的原因,可能是错误的
我们提供静态内容(75% 或更高)和脚本内容的混合服务,并且脚本内容通常占用大量处理器资源,因此在两者之间找到适当的平衡非常重要;从长远来看,我们希望将静态内容移到其他地方以优化两台服务器,但我们的软件目前还没有为此做好准备
如果有人有任何想法,我很乐意提供更多信息,另一个需要注意的是,这是一个高可用性生产安装,因此我对一次又一次的调整持谨慎态度,这就是为什么我还没有KeepAlive
自己玩过像价值这样的东西。
答案1
首先我承认我不太了解在云端运行东西 - 但根据我在其他地方的经验,我认为这个网络服务器配置反映的流量相当低。运行队列如此之大表明没有足够的 CPU 来处理它。运行队列中还有什么?
我们可能允许了太多的 KeepAlive 请求
不 - keeplive 仍然能提高性能,现代浏览器非常聪明,知道何时进行管道传输以及何时并行运行请求,尽管 5 秒的超时时间仍然相当长,而且你有一个很多服务器等待时间 - 除非您遇到严重的延迟问题,否则我建议将其降低到 2-3。这应该会缩短运行队列。
如果您尚未在 Web 服务器上安装 mod_deflate - 那么我建议您这样做 - 并将 ob_gzhandler() 添加到您的 PHP 脚本中。您可以将其作为自动添加项执行:
if(!ob_start("ob_gzhandler")) ob_start();
(是的,压缩会使用更多的 CPU - 但是您应该通过让服务器更快地退出运行队列/处理更少的 TCP 数据包来节省总体 CPU - 而且作为奖励,您的网站也会更快)。
我建议为 MaxRequestsPerChild 设置一个上限 - 比如说 500。这只会在出现内存泄漏时允许进程进行一些周转。您的 httpd 进程看起来非常庞大 - 确保您已删除所有不需要的 apache 模块,并确保您使用良好的缓存信息提供静态内容。
如果您仍然看到问题,那么问题可能出在 PHP 代码中(如果您切换到使用 fastCGI,这应该是显而易见的,而不会造成任何重大的性能损失)。
更新
如果静态内容在各个页面上差异不大,那么可能值得尝试一下:
if (count($_COOKIE)) {
header('Connection: close');
}
PHP 脚本也是如此。
答案2
您应该考虑安装异步反向代理,因为 W 状态的进程数量也相当高。您的 Apache 进程似乎花费大量时间通过网络向慢速客户端发送内容,但网络因此被阻止。Nginx 或 lighttpd 作为 Apache 服务器的前端可以大大减少 W 状态的进程数量。是的,您应该限制 keepalive 请求的数量。也许值得尝试关闭 keepalive。
顺便说一句,107 个 Apache 进程对于 22 rps 来说太高了,我仅使用 5 个 Apache 进程就能提供 100-120 rps。下一步可能是分析您的应用程序。
答案3
您的 vmstat 中有两行显示您的 CPU 等待时间相当长,并且在这两行周围,您进行了相当多的写入 (io - bo) 和上下文切换。我会查看写入阻塞的原因以及如何消除这种等待。我认为最大的改进在于改进磁盘 IO。检查 syslog - 将其设置为异步写入。确保您的控制器的写入缓存正常工作(检查它 - 您的电池可能坏了)。
Keepalive 不会导致您的性能问题,如果您不在前面运行缓存,它可以节省您设置连接的时间。您可以稍微增加 MaxSpareServers,以便在关键时刻您不必等待所有分叉。
答案4
第一个建议:禁用 keepalive。我只在能够确定特定情况时才需要它,即启用 Keepalive 后性能会提高,但总体而言每秒请求数会减少。
第二个建议:设置 MaxRequestsPerChild。我在这里回应 symcbean,它将有助于在发生内存泄漏时进行进程翻转。500 是一个很好的起点。
第三个建议:增加 MaxClients。对此的一个大致计算是(物理内存 - 非 httpd 进程使用的内存)/每个 httpd 进程的大小。根据 httpd 的编译方式,此数字最大为 255。我将 250 用于我的公共服务器,以应对 google/yahoo/MS 抓取系统。
第四条建议:增加 MaxSpareServers:例如 4-5x MinSpareServers。
除非这些建议失败,否则我会研究使用反向代理或 memcache 为 DB 实现负载平衡。