从另一个 php 文件执行 php 文件使用太多 CPU
Executing php file from another php file uses too much CPU
我已经阅读了关于 SO 的其他标题类似的问题,但这不是这个问题的内容。我知道如何从另一个 PHP 脚本执行 PHP 脚本。问题是,当我这样做时,它使用了太多 CPU。我想知道如何减少这个。
我有一个名为 index.php 的简单 front-controller-like 脚本。它处理来自客户端的 GET 请求,并根据传递的 "action" 参数,将请求发送到适当的文件进行处理。例如,这是一个客户端请求:
xhttp.open("GET", serverURL + "?action=doSomething" + "&userID=" + user.ID + "&time=" + lastServerTime, true);
index.php 有一个数组,将 "action" 参数映射到适当的文件:
exec('php ' . $url_map[$action] . ' "' . $parameter1 . '"' . ' "' . $parameter2 . '" 2>&1', $output, $return_value);
出于测试目的,我创建了一个 PHP 脚本,该脚本除了测量 CPU 利用率并将其转储到日志文件外什么都不做:
<?php
function varDumpToFile($parameter1) {
$file = 'log.txt';
$dump = $parameter1;
$output = print_r($dump, true);
file_put_contents($file, $output, FILE_APPEND | LOCK_EX);
}
varDumpToFile(`ps -eo pcpu,pid,user,args --no-headers| sort -t. -nk1,2 -k4,4 -r |head -n 5`);
?>
这会生成如下所示的日志文件:
9.0 3123052 user /opt/cpanel/ea-php56/root/usr/bin/php cputest.php 10 147424 1537625595
显然,PHP 脚本不应占用 CPU 的 9% 来执行。为了比较,我 运行 相同的脚本通过 GET 请求直接访问它:
0.1 3186198 user lsphp:ic_html/dev/php/cputest.php
0.1% 比较像。但是为什么从另一个 PHP 脚本调用这个 PHP 脚本会使用这么多 CPU?是不是因为我执行PHP的时候要执行一个PHP的"new instance",开销很大?如果是这样,有没有办法使用 PHP 的 "already running" 实例来执行 PHP 脚本?或者还有其他方法吗?
我总是说"when in doubt, look at PHP source code"。 In here,例如。在执行 exec
时,您必须分叉进程、创建新流、从输入缓冲区读取等
此外,虽然 PHP 是一种编译语言,但对于新分叉的进程,您必须 运行 操作码编译器来生成操作码(类似于 Java 字节码的指令)和然后执行那些。您可以阅读所有相关内容 here。最后你 运行 编译器两次,分别为每个 fork。
它值你 CPU 的 9% 吗?我不知道。可能是。也许不吧。谁知道呢
"Better solution"?升级到 PHP 的最新版本。 PHP 5.6 不再受支持,安全更新将在 3 个月后停止。更好的解决方案 - 在不使用 exec
的情况下保留正常的面向对象和可维护的代码。 IMO,像你这样玩 exec
没问题。但如果这是你的生产代码,我为那些会在你之后维护你的代码的人祈祷。
无论您 运行 您的应用程序是 mod_php
还是 fpm
,它们都依赖于让工作进程准备好管理您的请求。进程管理是内置的:他们会尽最大努力让尽可能多的工人闲置,就像你指定的那样,并重用他们来避免这个问题,必须在最不希望的时刻分叉进程。
不仅执行新进程有开销,而且执行环境也会完全不同。如果您查看 php 配置,将会有几个 php.ini
文件,每个文件对应一个特定环境。这意味着一个环境可以启用不同的模块或完全不同的配置。将 cli 脚本 max_execution_time
或 memory_limit
设置为无限制的情况并不少见。这会影响服务器上的资源使用,但维护起来也很痛苦。
此外,由于您的脚本将 运行 运行在不同执行环境中的全新进程中,因此无法访问某些变量(如 $_SERVER
或 $_POST
) 或发送 headers.
等功能
还有一个叫做共享内存的东西。正如@Alex 提到的,必须编译脚本。如果您启用了操作码缓存(您应该启用),则字节码在编译时会被缓存,如果生成的字节码已经存在,则可以跳过此编译过程。为此,您 需要 有一个持久的 运行ning 进程来保留此内存。如果你正在创建一个新进程,它不能访问这个共享区域,必须自己编译。
我已经阅读了关于 SO 的其他标题类似的问题,但这不是这个问题的内容。我知道如何从另一个 PHP 脚本执行 PHP 脚本。问题是,当我这样做时,它使用了太多 CPU。我想知道如何减少这个。
我有一个名为 index.php 的简单 front-controller-like 脚本。它处理来自客户端的 GET 请求,并根据传递的 "action" 参数,将请求发送到适当的文件进行处理。例如,这是一个客户端请求:
xhttp.open("GET", serverURL + "?action=doSomething" + "&userID=" + user.ID + "&time=" + lastServerTime, true);
index.php 有一个数组,将 "action" 参数映射到适当的文件:
exec('php ' . $url_map[$action] . ' "' . $parameter1 . '"' . ' "' . $parameter2 . '" 2>&1', $output, $return_value);
出于测试目的,我创建了一个 PHP 脚本,该脚本除了测量 CPU 利用率并将其转储到日志文件外什么都不做:
<?php
function varDumpToFile($parameter1) {
$file = 'log.txt';
$dump = $parameter1;
$output = print_r($dump, true);
file_put_contents($file, $output, FILE_APPEND | LOCK_EX);
}
varDumpToFile(`ps -eo pcpu,pid,user,args --no-headers| sort -t. -nk1,2 -k4,4 -r |head -n 5`);
?>
这会生成如下所示的日志文件:
9.0 3123052 user /opt/cpanel/ea-php56/root/usr/bin/php cputest.php 10 147424 1537625595
显然,PHP 脚本不应占用 CPU 的 9% 来执行。为了比较,我 运行 相同的脚本通过 GET 请求直接访问它:
0.1 3186198 user lsphp:ic_html/dev/php/cputest.php
0.1% 比较像。但是为什么从另一个 PHP 脚本调用这个 PHP 脚本会使用这么多 CPU?是不是因为我执行PHP的时候要执行一个PHP的"new instance",开销很大?如果是这样,有没有办法使用 PHP 的 "already running" 实例来执行 PHP 脚本?或者还有其他方法吗?
我总是说"when in doubt, look at PHP source code"。 In here,例如。在执行 exec
时,您必须分叉进程、创建新流、从输入缓冲区读取等
此外,虽然 PHP 是一种编译语言,但对于新分叉的进程,您必须 运行 操作码编译器来生成操作码(类似于 Java 字节码的指令)和然后执行那些。您可以阅读所有相关内容 here。最后你 运行 编译器两次,分别为每个 fork。
它值你 CPU 的 9% 吗?我不知道。可能是。也许不吧。谁知道呢
"Better solution"?升级到 PHP 的最新版本。 PHP 5.6 不再受支持,安全更新将在 3 个月后停止。更好的解决方案 - 在不使用 exec
的情况下保留正常的面向对象和可维护的代码。 IMO,像你这样玩 exec
没问题。但如果这是你的生产代码,我为那些会在你之后维护你的代码的人祈祷。
无论您 运行 您的应用程序是 mod_php
还是 fpm
,它们都依赖于让工作进程准备好管理您的请求。进程管理是内置的:他们会尽最大努力让尽可能多的工人闲置,就像你指定的那样,并重用他们来避免这个问题,必须在最不希望的时刻分叉进程。
不仅执行新进程有开销,而且执行环境也会完全不同。如果您查看 php 配置,将会有几个 php.ini
文件,每个文件对应一个特定环境。这意味着一个环境可以启用不同的模块或完全不同的配置。将 cli 脚本 max_execution_time
或 memory_limit
设置为无限制的情况并不少见。这会影响服务器上的资源使用,但维护起来也很痛苦。
此外,由于您的脚本将 运行 运行在不同执行环境中的全新进程中,因此无法访问某些变量(如 $_SERVER
或 $_POST
) 或发送 headers.
还有一个叫做共享内存的东西。正如@Alex 提到的,必须编译脚本。如果您启用了操作码缓存(您应该启用),则字节码在编译时会被缓存,如果生成的字节码已经存在,则可以跳过此编译过程。为此,您 需要 有一个持久的 运行ning 进程来保留此内存。如果你正在创建一个新进程,它不能访问这个共享区域,必须自己编译。