PHP-FPM 在执行繁重工作时用户过多时崩溃

Posted

技术标签:

【中文标题】PHP-FPM 在执行繁重工作时用户过多时崩溃【英文标题】:PHP-FPM crashes when having too many users while doing a heavy job 【发布时间】:2016-05-20 13:26:33 【问题描述】:

我有一个运行 Apache/2.2.22 (Debian)、php 5.6.17 作为 FPM 和 mysql 5.6.25 的服务器。

该项目使用名为 Redaxo 的 CMS 运行(我认为这并不重要,但我还是会告诉你)。在 Redaxo 中,有些功能需要一些时间(例如,删除缓存并重建它需要 1-2 分钟)。此时,当其他用户访问网站时,FPM 崩溃并显示500 Internal Server Error,我必须多次重新加载页面,直到服务器错误消失并完成该过程。

我注意到只有当太多用户同时在网站上并且只有在完成繁重的操作时才会发生这种情况。

10 个用户同时上网 = 没问题 10 个用户同时上网,而缓存删除 = 每个人的 500 错误。

我通过禁止除我之外的所有人访问该网站来检查这一点(.htaccess 拒绝/允许使用 ip)。然后我做了繁重的操作,没有问题。一旦多个人再次访问该站点,问题就又出现了。

可能是什么?您需要我提供什么信息?

这些值在php-fpm.conf 中设置(未注释)

[global]
pid = /run/php5-fpm.pid
error_log = /var/log/php5-fpm.log
emergency_restart_threshold = 0
include=/etc/php5/fpm/pool.d/*.conf

这些值在项目特定fpm.conf中设置(未注释)

[projectname]
user = projectname
group = projectname

listen = /var/run/php5-fpm-projectname.sock
listen.owner = projectname
listen.group = projectname
listen.mode = 0660

pm = dynamic
pm.max_children = 150
pm.start_servers = 10
pm.min_spare_servers = 10
pm.max_spare_servers = 30

chdir = /

php_value[upload_max_filesize] = 128M
php_value[max_post_size] = 128M
php_value[max_execution_time] = 180
php_value[memory_limit] = 256M

脚本失败时对 MySQL 和文件创建有很大帮助吗?但是它很大,所以我不确定是否应该在这里发布?或者如果它甚至是问题?

apache 错误日志说明了这一点

[Tue Feb 09 10:54:01 2016] [error] [client IP] (104)Connection reset by peer: FastCGI: comm with server "/fcgi-bin-php5-fpm-projectnmae" aborted: read failed
[Tue Feb 09 10:54:01 2016] [error] [client IP] FastCGI: incomplete headers (0 bytes) received from server "/fcgi-bin-php5-fpm-projectnmae"

或者这个

[Tue Feb 09 11:00:46 2016] [error] [client IP] FastCGI: incomplete headers (0 bytes) received from server "/fcgi-bin-php5-fpm-projectname"
[Tue Feb 09 11:00:48 2016] [error] [client IP] (104)Connection reset by peer: FastCGI: comm with server "/fcgi-bin-php5-fpm-projectname" aborted: read failed

fpm-log 表示以下内容。当然总是不同的时间安排

[10-Feb-2016 09:40:59] WARNING: [pool projectname] child 10970 exited on signal 7 (SIGBUS) after 50.186611 seconds from start
[10-Feb-2016 09:40:59] NOTICE: [pool projectname] child 11092 started

有时候里面会有这样的警告

[09-Feb-2016 11:00:41] WARNING: [pool projectname] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning 8 children, there are 0 idle, and 6 total children
[09-Feb-2016 11:00:42] WARNING: [pool projectname] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning 16 children, there are 0 idle, and 7 total children

这里有更多调试信息

[18-Feb-2016 17:42:01] WARNING: [pool projectname] child 9088 exited on signal 7 (SIGBUS) after 70.130564 seconds from start
[18-Feb-2016 17:42:01] NOTICE: [pool projectname] child 9205 started
[18-Feb-2016 17:43:55] WARNING: [pool projectname] child 9099 said into stderr: "NOTICE: PHP message: PHP Notice:  Undefined offset: 1181 in /var/www/projectname/htdocs/redaxo/include/classes/class.ooarticle.inc.php on line 44"
[18-Feb-2016 17:43:55] WARNING: [pool projectname] child 9099 said into stderr: "NOTICE: PHP message: PHP Warning:  Invalid argument supplied for foreach() in /var/www/projectname/htdocs/redaxo/include/classes/class.ooredaxo.inc.php on line 134"
[18-Feb-2016 17:43:55] WARNING: [pool projectname] child 9099 exited on signal 7 (SIGBUS) after 183.838886 seconds from start
[18-Feb-2016 17:43:55] NOTICE: [pool projectname] child 9330 started
[18-Feb-2016 17:44:00] WARNING: [pool projectname] child 9101 exited on signal 7 (SIGBUS) after 188.987954 seconds from start
[18-Feb-2016 17:44:00] NOTICE: [pool projectname] child 9336 started

【问题讨论】:

apache 和 php-fpm 日志中是否有任何关于崩溃的信息? 抱歉,我忘记添加了。给我一分钟 什么触发了缓存重建?可以移到cli脚本吗? 有一个未解决的错误报告,指出包含当前正在写入的文件可能会发出此错误(bugs.php.net/bug.php?id=52752,仍然存在于 php7 中)。您可能想要设置静态缓存,或将页面设置为维护模式。 我不确定内部结构。也可能受到影响。在缓存删除期间,服务器是否处于最佳性能?我知道劫持缓存机制很困难,但可能的解决方案是编写一个长时间运行的脚本,手动删除缓存,一次一页,速度较慢。您可以为用户保留一些 CPU,但缓存重建将花费更长的时间。这里有一些手动缓存删除的提示redaxo.org/de/wiki/index.php?n=R2.Caching(虽然是德语) 【参考方案1】:

我已经看了几天了,最后决定增加我的 2 美分的价值。

我已经使用 FPM 很长时间了,这是一件很棒的事情,但使用它获得可扩展的配置是另一回事。有很多问题可能会导致您的问题,但我有一个怀疑。

我想关注您的输出中出现的 PHP 错误,因为它们表明出现了不应该出现的错误。我想知道,当您清除缓存和用户浏览网站时,他们是否同时提取不完整的数据,因为某些信息已被删除或正在重建。您甚至可能会看到缓存被删除的情况,同时新的东西正在被缓存。我没有查看用于缓存删除的 CMS 代码,但您显示的 PHP 错误似乎表明在此过程中正在获取一些无效数据。

要尝试的一件事是在缓存删除之前显式锁定表,然后释放它们。这样,用户在删除内容时无法读取或写入数据。在您调用清除缓存的任何脚本中,尝试添加查询LOCK TABLES articles WRITE, othertable WRITE, anyothertable WRITE。这将防止其他会话(用户)在清除缓存时读取或更新这些表。

用户很不耐烦,如果他们尝试加载页面并且没有给他们任何反馈,他们经常会尝试重新加载,或者返回并单击其他链接。这会导致 FPM 进程的数量增加。如果 10 个用户刷新 5 次,那么您现在有 50 个额外的进程在运行并且还会挂起,这会使情况变得更糟。

-- 其他东西

在 Apache 中增加 ProxyTimeout 或超时。如果您有一个可以运行一段时间的脚本,如果 Apache 在一定时间内没有取回任何数据(这没关系),它将终止连接。如果清除缓存需要 5 分钟,并且 PHP 在完成之前没有发回任何内容,并且 Apache 的超时时间为 120 秒,那么它将在连接完成之前断开连接,从而导致您看到的超时。我有很多网站可以做长达 10 分钟的事情,所以我在 Apache 中的超时时间是 600 秒。这允许 PHP 请求在没有中断的情况下完成。

我注意到的另一件事是您正在使用 unix 域套接字进行 FPM 通信。这可能没问题,但它们在非常繁忙的站点上不能很好地扩展。我建议改用 TCP 套接字。 listen = 127.0.0.1:9000 然后您需要修改 Apache 以使用 tcp 而不是域套接字进行连接。

设置listen.backlog,以便在忙时可以将连接排队。您可能还需要使用 sysctl 调整内核值 net.core.somaxconn,因为它通常非常低。

Apache MPM:如果您还没有使用它,请切换到 MPM worker。由于您使用的是 FPM,因此对于 Apache,worker 是一个非常高效的 MPM,比 prefork(通常是默认值)要好得多。确保根据您的需要对其进行调整(即适当地设置服务器、线程和 MaxRequestWorkers)。

-- 关闭

我认为这里没有什么太复杂的事情,我首先要看的是确保缓存删除可以不间断地完成。即使这意味着用户会在几分钟内看到维护页面,或者他们的请求在完成之前被阻止了一小段时间,但如果它避免了 500 次错误和错误,这也是一个很小的代价。

老实说,我认为删除缓存和人们浏览存在问题,这会影响进程并使事情花费的时间超过必要或中断。

如果您有任何问题,请告诉我或随时与我联系。

【讨论】:

【参考方案2】:

除非您有足够的可用 RAM(例如超过 16GB 可用),否则我建议您的资源不足,这会导致 500 错误。

您的配置是说您最多可以生成 150 个 PHP-FPM 进程,每个进程可以使用 256MB 内存 — 仅此一项就可以使 PHP-FPM 服务器使用超过 38GB 的​​内存,如果不可用,它将导致 500 错误。

计算每台服务器可以使用多少内存,然后正确设置。此 CMS 是否需要高达 256MB 的内存?它可以用更少的内存(比如 32MB)运行吗?如果 MySQL、Apache 和 nginx 在同一台服务器中,请将各自使用的内存分开,然后为 pm.max_childrenphp_value[memory_limit] 设置适当的值。

请注意,资源不足是系统范围的,所以如果你的 PHP 进程使用了​​所有可用的内存,MySQL 可能最终会因为资源耗尽而崩溃(这可能是找不到记录的原因)。

如果你能说出你有多少可用内存,我可以帮你配置这些数字。

在您发出缓存删除之前知道有多少可用内存以及在运行时有多少可用内存也很好 - 它可能确实使用了太多内存并窒息其他进程(如果它使用 PHP -CLI,它可能没有内存限制)。

【讨论】:

为了安全起见,最好也降低这些数字:php_value[upload_max_filesize]php_value[max_post_size]php_value[max_execution_time] — 除非您希望收到大文件上传(最大 128MB) ,因为它将添加到 memory_limit 之上,并且可能会使您的网站非常容易受到 DoS 攻击。 实际上,空闲的 FPM 进程将消耗更少的内存 (5-15MB),而正在使用的可能会消耗 15-50,但除非涉及大量数据,否则很少会更多。不过,这仍然是一个好点。考虑到正在运行的其他事物(MySQL、Apache 等),服务器的数量应设置为不超过服务器中的内存量。【参考方案3】:

每次服务器挂起时,如果 php 和/或 Apache 达到限制,您会看到不同的错误。

如果您的主机是 Unix/Linux,您能否在 CMS 执行任何繁重的工作时检查命令 $ top 的结果?

如果你看到内存耗尽,很大一部分交换内存被填满并且 CPU 在顶部,尝试调整 php.ini 的 memory_limit 来分配资源。但可能您需要增加资源、内存和 CPU。

如果内存和 CPU 不忙,可能是你分配给 php.ini 的内存少了。您可以运行更多的 php-fpm 工作程序,增加每个进程的内存限制,...参见http://linuxbsdos.com/2015/02/17/how-to-reduce-php-fpm-php5-fpm-ram-usage-by-about-50/。另请查看 Apache 内存和 CPU 配置。

【讨论】:

很遗憾,我们有足够的免费资源。我已经检查了顶部,没有任何问题 好的,所以查看PHP警告,似乎article_id 1181不存在。可能是每个 php-fpm 工作人员在 redaxo/include/classes/class.ooarticle.inc.php:44 中挂断,最多等待 180 秒。这里pastebin.com/4GniWSiJ 是一个示例,说明如何避免错误并记录有关 Redaxo 4.6.2 的 class.ooarticle.inc.php:44 中异常的上下文信息。替换文件以获取日志并检查泄漏是否真的在此异常中开始。您应该在之后恢复原始文件。 嘿。我在数据库中检查了 ID 为 1181 的文章,但我必须告诉你它存在。你还觉得这有关系吗? 您能否复制您的网站和数据库并替换原始的 pastebin 文件以查看:1181 是否又丢失了?还有其他id吗? 很多时候,应用程序的错误会使内存过载或花费时间并导致 php 工作人员挂断。增加资源或优化配置可以绕过泄漏,但修复应用程序是最好的解决方案。【参考方案4】:

这可能只是您的 MySQL 服务器的某些锁定问题的影响。

您必须在延迟期间连接到您的 MySQL 主机。

如果无法连接,说明您的 MySQL 服务器或用户的并发连接数已用完

如果可以连接,则必须查看 mysql 命令“show processlist”返回的内容。现在您有 2 个选择:

许多“等待查询缓存锁定”:这将需要您更改一些 MySQL 服务器配置。 (这可能是由于查询缓存过大造成的

您的请求占用了所有资源,您必须对其进行优化。

【讨论】:

进程运行时我已连接。我会检查后者 嗨,我有很多请求,但它们的状态是“正在创建排序索引”。没有像“等待查询缓存锁定”这样的东西 所以你显然属于最后一类。您有一个需要优化的请求或表格。对于这些表,您必须定义一个多列索引,该索引以用于排序的列开始,以用于选择/连接的列结束。 ALTER TABLE x ADD INDEX(sort_column1, sort_column2, where_column1, where_column2, .) 嘿亚当。查询来自 CMS。我已经检查了数据库和正在执行的查询。数据库表具有这些查询的索引

以上是关于PHP-FPM 在执行繁重工作时用户过多时崩溃的主要内容,如果未能解决你的问题,请参考以下文章

当某些并行进程执行繁重的工作时,数据流会阻塞

在 react (Node.JS) 中使用繁重/耗时的函数

VBA 在运行时崩溃,但在单步执行时工作

从并行线程在主 Maya 线程上执行代码

发送过多串行数据时,Digis XBee 系列 2 固件冻结/崩溃。可靠性很差

Nginx+PHP-FPM远程命令执行_CVE-2019-11043