我们正在运行具有以下配置的服务器:Nginx + FastCGI(PHP-FPM)+ PHP 7.3 + MySQL 5.7。但是,我们在长时间运行 PHP 脚本时遇到一些问题,因此我们设置了以下限制:
php.ini
...
max_execution_time = 60
max_input_time = 60
...
pool.d/xxx.conf (池配置)
...
pm = dynamic
pm.max_children = 20
pm.max_requests = 500
request_terminate_timeout = 300
...
nginx 虚拟主机
location ~ \.php$ {
...
fastcgi_connect_timeout 300s;
fastcgi_read_timeout 300s;
fastcgi_send_timeout 300s;
fastcgi_keep_conn on;
...
}
当我们检查 php-fpm 日志时,我们发现一些工作进程被 SIGKILL/SIGTERM 信号触发,但这里重要的是,在哪个时间之后,工作进程被终止。这个时间大于在不同配置文件中设置的所有可能的超时时间(见上文)。例如,下面的脚本(4 月 20 日 19:09:24)在 400 秒后超时,并在 428 秒后终止。第二个脚本(4 月 20 日 19:05:34)在 427 秒后终止,但只处理了 1 个请求(根据 FPM 状态页面)。因此,一个请求的持续时间为 427 秒……但 php.ini 超时时间为 60 秒,其他超时时间(nginx、php-fpm)设置为 300 秒,不会更长。
Apr 20 19:09:24 srv01 php-fpm[21093]: [WARNING] [pool xxx] child 31032, script '/example.com/index.php' (request: "GET /index.php") execution timed out (399.965025 sec), terminating
Apr 20 19:09:24 srv01 php-fpm[21093]: [WARNING] [pool xxx] child 31032 exited on signal 15 (SIGTERM) after 428.563463 seconds from start
...
Apr 20 19:05:34 srv01 php-fpm[21093]: [WARNING] [pool xxx] child 30951 exited on signal 9 (SIGKILL) after 427.516062 seconds from start
Apr 20 19:05:34 srv01 php-fpm[21093]: [WARNING] [pool xxx] child 30985 exited on signal 9 (SIGKILL) after 334.407532 seconds from start
Apr 20 19:05:34 srv01 php-fpm[21093]: [WARNING] [pool xxx] child 30960 exited on signal 9 (SIGKILL) after 411.221728 seconds from start
Apr 20 19:05:34 srv01 php-fpm[21093]: [WARNING] [pool xxx] child 31031 exited on signal 9 (SIGKILL) after 210.926257 seconds from start
所以我的问题是,这怎么可能呢?我们是否错过了其他一些配置指令(在 nginx/php-fpm 中)来限制这些脚本的最大持续时间?或者这些问题是否应该与此处提到的一些 PHP-FPM/Nginx 通信问题有关:
- https://github.com/perusio/drupal-with-nginx/issues/55(这与“fastcgi_keep_conn”与“动态” PHP-FPM 进程管理器有关)
- https://bugs.php.net/bug.php?id=63395
看起来问题不在 MySQL 中(当记录长时间运行的脚本时,所有进程都处于空闲状态)。
我们也在致力于优化应用程序(将来应该会消除这些长时间运行的脚本),但在此之前,我们需要加强服务器,因为我们可以毫无问题地运行应用程序。因此,我们想知道我们还应该尝试什么来限制这些脚本的最大运行时间。
谢谢你的帮助!
答案1
我想到三种可能性,注册关闭函数(),通过进程分叉复制并返回 pcntl_fork(),最后——如何进行时间记账。
关于关机功能的页面上有一条注释:
关闭函数的运行时间与 max_execution_time 跟踪的时间是分开的。这意味着即使进程因运行时间过长而终止,仍会调用关闭函数。此外,如果在关闭函数运行时 max_execution_time 用尽,则不会终止进程。
因此,如果有关闭功能,max_execution_time 将不会停止该功能。
另一方面,分叉一个进程将允许子进程任意运行,因此在允许子进程时必须小心。
最后,其中一些计时器仅适用于 CPU 时间,因此如果进程处于“等待”状态且实际上没有执行任何工作,则计时器不会为其运行。sleep()
例如,调用所花费的时间不会计入 max_execution_time 限制,因此它可以继续休眠很长时间。