![apache2.4 debian 9 上的随机 CONNECTION_RESET](https://linux22.com/image/716591/apache2.4%20debian%209%20%E4%B8%8A%E7%9A%84%E9%9A%8F%E6%9C%BA%20CONNECTION_RESET.png)
我的服务器出现了一些奇怪的行为,我就是找不到原因。我到处找了。
我将向能够解决这个问题的人支付价值 200 美元的比特币。
问题:
当从 apache 请求任何资源(页面、图像、css、js)时,有时需要很长时间才能响应。大约一半的时间,连接会被重置。(在 Chrome 上:net::ERR_CONNECTION_RESET)这种情况很少发生,随机发生,并且绝对不可预测。更令人困惑的是,虽然一个请求似乎挂起了,但我可以发出其他完美运行的请求。
关于服务器:
我在 Debian 9 上运行 apache2.4 mpm-prefork 和 php7.0。apache 模块使用 mod_rewrite 和来自 certbot 的 ssl 证书。在某些情况下,php 会调用 inkscape 将 svgs 渲染为 png。
服务器负载非常低 (0.02) 并且只运行 apache。
检查内容:
- 检查了所有服务器日志。(系统日志、apache 日志)- 没有
- 增加了 apache mpm-prefork 限制 - 不行
- 检查了可能的 DNS 问题 - 没有结果
- 我甚至搬到了一个全新的根服务器(在不同的提供商上)——情况仍然一样
我继续用Wireshark分析tcp流量,发现一些可疑行为。当连接冻结时,会出现一些TCP无序、重传和ACKed未见分段数据包...但我没有必要的底层知识来判断发生了什么。
任何提示都是大大感谢!
编辑:
这是 mpm_prefork 配置:
<IfModule mpm_prefork_module>
StartServers 10
MinSpareServers 10
MaxSpareServers 50
MaxRequestWorkers 300
MaxConnectionsPerChild 0
</IfModule>
编辑编辑:
当这种情况再次发生时,我很幸运,在服务器和客户端上都运行了 tcp 嗅探器。以下是 pcap 文件,截取到最后约 30 秒。
如果有知识的人可以快速看一眼并告诉我发生了什么,我会很高兴。
编辑编辑编辑:
我设法使错误可重现,至少在 KeepAlive 开启的情况下。当请求完成且内容已提供时,tcp 连接将在 5 秒后以 FIN-ACK 关闭。在 FIN-ACK 之后 5-12 秒的时间窗口内发出另一个请求时,连接将冻结。
然而,关闭 KeepAlive 后,这种情况不再发生,尽管同时加载多个资源时错误发生得更频繁。但之后就无法重现了。
答案1
我会检查服务器和客户端之间传输的 TCP 数据包的大小。如果数据包大小接近 1500,则有可能因为多种原因被丢弃:
如果数据包上设置了 DNF 位,并且数据包在某处被分割,则这可能是导致数据包被丢弃的问题
如果 MTU 设置为 1500,并且数据包正在通过隧道、加密等,这会导致数据包中添加额外的标头,那么这也会导致数据包丢失。尝试将您正在使用的接口两端的 mtu 设置为低于 1500 的值,可能是 1420 甚至更低。
答案2
非常确定我发现了这个问题:-)因为我刚刚遇到了同样的事情。
1. 原因
我认为你有二或更多服务于端口 80(或 443,如果是 SSL 连接)的进程。您可以按如下方式检查,此处使用端口 80 的命令以及我出现问题的系统的输出:
# netstat -tupan | grep ":80.*LISTEN"
Proto Recv-Q Send-Q Local Foreign State PID/Program name
Address Address
tcp6 0 0 :::80 :::* LISTEN 22718/apache2
tcp6 0 0 :::80 :::* LISTEN 1794/apache2
两个进程通过同一个端口为同一个 IP 地址提供服务确实是有可能的,可以使用端口选项SO_REUSEADDR
,SO_REUSEPORT
请参见这里和这里(关于“Linux >= 3.9”的部分)。
内核所做的SO_REUSEPORT
就是以非确定性的方式将传入的 TCP 连接分发给为该端口提供服务的进程。一个进程是正确处理请求的 Apache,另一个进程是“其他”进程,它永远不会回答任何问题。在我的例子中,它是另一个 Apache2 进程。
2.解决方案
如果您有两个 Apache 进程,首先找出其中哪个是“僵尸”。为此,请停止常规 Apache 服务器 (
service apache2 stop
) 并检查哪个进程仍存在 (netstat -tupan | grep ":80.*LISTEN"
)。这就是“僵尸”。记下它的 PID。要了解有关谁或什么启动了这个“僵尸”进程的更多信息:
cat /proc/<pid>/loginuid
使用该“僵尸”进程的 PID执行。如果显示4294967295
,则表示是由系统而不是用户启动的(原因)。否则,它是您可以查找的用户的 UID。执行
ps auxf
并确定“僵尸”进程的进程启动时间。如果它与系统启动时间相匹配,则意味着该进程在启动时以某种方式启动。
为了(也许)了解有关此“僵尸”进程内部发生的情况的更多信息,您可以使用 附加到它
strace
。这将创建大量难以阅读的日志,但由于重现此“僵尸”进程的问题可能并不容易,因此在我们终止该进程之前至少收集其中一些日志(尤其是发送到该进程的 HTTP 请求)似乎不错。您将使用进程的 PID 执行,而不是$PID
:strace -o strace.log -f -p $PID
为了暂时解决该问题,请终止“僵尸”进程,并为 提供其 PID
$PID
:kill $PID
或者如果需要kill -9 $PID
。检查该“僵尸”进程在重新启动后是否再次启动并运行,如果是,则必须调查并修复导致那。
3. 重现原因
手动创建一个 Apache2 “僵尸”进程是可能的(但并非易事),它将与常规 Apache 服务器并行运行,并且“不回答任何问题”。以下是几乎但不完全完整的说明:
创建相关配置文件的副本:
cp /etc/apache2/envvars /etc/apache2/envvars-zombie cp /etc/apache2/apache2.conf /etc/apache2/apache2-zombie.conf
编辑
/etc/apache2/envvars-zombie
脚本开头的并静态设置SUFFIX="-zombie"
,覆盖其中的条件赋值。编辑
/etc/apache2/apache2-zombie.conf
并阻止包含任何 VirtualHost 配置文件。就我而言,我会将相应的行修改为:# IncludeOptional sites-enabled/
确保
apache2-zombie.conf
文件中包含默认监听端口。在我的例子中,这已通过 实现Include ports.conf
。创建 Apache2 新实例所需的锁文件和日志目录,并使新 Apache2 运行时用户可以访问它们:
mkdir /var/log/apache2-zombie chown www-data /var/log/apache2-zombie/ mkdir /var/lock/apache2-zombie chown www-data /var/lock/apache2-zombie/
现在您应该能够按如下方式启动“僵尸”Apache 进程:
cd /etc/apache2/ source envvars-zombie /usr/sbin/apache2 -f apache2-zombie.conf -k start
确认现在确实有第二个进程在 Apache2 标准端口上运行:
netstat -tupan | grep ":80.*LISTEN"
。第二台 Apache2 服务器还不是“僵尸”,因为它仍然会回答“404 Not Found”或(因为我们没有设置 SSL)在端口 443 上发出请求时导致 SSL 错误。但您已经可以观察到以下效果:一些请求以不确定的方式发送到这个新服务器并导致这些错误。(我在实践中已经达到了这一点……)
要创建“合适的”僵尸 Apache,请设置一个简单的脚本,该脚本将接受 HTTP 请求,然后
sleep()
在几分钟内不执行任何操作( ),以让浏览器放弃,让 TCP 连接超时。为 Apache 默认主机安装它。这样,它将用于对端口的所有 HTTP 请求,因为我们禁用了所有 VirtualHost 配置,因此 Apache 无法为任何请求找到更合适的主机,并将选择默认主机。