什么原因会导致 Apache 服务器在“发送回复”过程中锁定?

什么原因会导致 Apache 服务器在“发送回复”过程中锁定?

我正在运行一个 CentOS 服务器作为 LAMP 堆栈,为自定义 php 应用程序提供服务。它似乎会随机地变慢。查看服务器状态页面,我看到 PID 列表被锁定,其中包含多个相同的 ajax 调用,这些调用都是从一个用户的客户端 IP 请求的。(IP 会发生变化,但始终只有一个)

我看到“M”参数状态为 W "Sending Reply",这是什么意思?

缓慢问题通常会在 5 分钟到 1 小时后自行解决。然而前几天我决定执行:

service httpd restart graceful.

这彻底解决了问题——持续了 10 分钟。下面是 14 分钟后的服务器状态,缓慢且锁定。似乎请求数迅速增加到 50,服务器速度变慢。

需要考虑的要点:

  • 多个请求总是来自同一个IP
  • 请求最长持续时间 (SS) 约为 200 秒
  • 所有请求都转到一个 ajax.php 脚本
  • 速度变慢有时几周都不会发生,但几天内就会发生几次
  • 用户在浏览器中只打开了几个服务器地址的标签页,总共 25 个
  • 显然最严重的问题发生在下午晚些时候

在此处输入图片描述

所以我的问题是什么原因可能导致这种锁定,为什么所有的请求都是这样"Sending Reply"

这是 httpd.conf

<IfModule worker.c>
StartServers         2
MaxClients          50
MinSpareThreads     25
MaxSpareThreads     75
ThreadsPerChild     25
MaxRequestsPerChild  0
</IfModule>

答案1

看起来这些连接卡住了很长一段时间(SS 是请求所花费的时间,其中一些甚至要花几分钟)。

我的直觉告诉我要查看数据库和 PHP 应用程序。检查池中是否有足够的可用连接,检查维护任务(完全真空可能会长时间锁定数据库!)并记录长查询以查看您是否正在执行可能锁定重要表的操作。这也可能是 PHP 脚本中的问题,导致它无法及时终止。

这里此页面提供了针对此类情况的一些有用的调试技巧。

答案2

鉴于您的情况下连接来自您的 LAN,所以不太可能是攻击,但就我而言,我有一个外部 IP(一次)在 wordpress 网站(最新)上执行相同的操作并得到此信息,甚至在以下情况下也是如此:

/wp-content/plugins/wpmarketplace/readme.txt

在我的服务器上不存在(大多数 GET 资源都不存在,并且有大量 txt 和 css 文件被 GET)。还有针对各种 php 文件的 POST 请求,导致同样的缓慢并最终冻结。

所以我的直觉是,这是一个编写得很差的脚本,用于检查易受攻击的网站,导致 DoS。或者它实际上可能是 DoS 而不是有缺陷的脚本,但是我已经很久没见过这样的脚本了,现在人们都在做 DDoS。

我目前正在编写一些脚本来控制这个问题。一旦我编写了这些脚本,我会回复,也许这会对某些人有所帮助。

最后编辑:

经过多次测试,我想我终于设法控制了一切。假设您创建了一个新脚本 /root/check_httpd.sh(解释见底部)

cnt=`ps -Af | grep httpd | grep -v rotatelogs | grep -v grep | wc -l`
now=`date +%Y-%m-%d_%H-%M`

# change the 40 below to something meaningful to your server
if [ $cnt -ge 40 ]
then
  /usr/bin/wget -q -O /root/apache_status_$now http://<your server here>/server-status
  /sbin/service httpd restart
fi

# change hda to your partition/disk which is being "killed" by httpd during the freeze
dsk=`/usr/bin/iostat -dx /dev/hda 5 2 | grep hda | tail -1 | awk '{print $12}'`

if (( $(echo "$dsk > 98" |bc -l) ))
then
  /bin/sleep 5
  dsk=`/usr/bin/iostat -dx /dev/hda 5 2 | grep hda | tail -1 | awk '{print $12}'`
  if (( $(echo "$dsk > 98" |bc -l) ))
  then
    /sbin/service httpd restart
  fi
fi

然后将其添加到 cron 中,如下所示:

0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58 * * * * /root/check_httpd.sh

别忘了

chmod +x /root/check_httpd.sh

以及解释。

因此,就我而言,最初,我注意到(在冻结期间)httpd 状态页面会显示许多“W”状态的 httpd 子进程,在各种资源上等待的时间各不相同,有些有效,有些无效。我花了很多时间尝试各种选项,以根据状态页面获得 90% 以上的场景,以查找服务器何时冻结且未处于重度使用状态。没有运气。但后来我发现,通常情况下,即使在“重度”负载下,我的 httpd 子进程数仍将低于 20-30(我的站点是“精简版”),所以我做了一些测试,发现在冻结期间,httpd 子进程数始终为 40(注意:您可以从该部分中删除状态 wget,它在那里是为了让您确认在重新启动期间,无论您选择的值计数,确实存在冻结。您手动检查)

但是,仅凭这一点还不够。我仍然遇到过服务器冻结超过 24 小时的情况,之后才会开始计算 40 次。经过进一步搜索,我发现 atop 实用程序是我在 putty 终端中运行的,因此每当服务器冻结时,我都可以看到究竟消耗了哪些资源。我注意到是硬盘。因此,需要对硬盘使用情况进行第二次检查,但正如您所知,硬盘使用情况时不时会激增,因此仅进行一次检查会导致误报。我所做的是等待几秒钟后再进行一次检查,然后才在需要时重新启动 httpd。

您需要在服务器上进行一些操作并微调阈值以使其适合您的环境和使用模式。

相关内容