10

首先,我远不是 Linux 专家,所以这可能是这里的问题,但无论如何,问题是:

我按照这里写的:http: //symcbean.blogspot.com/2010/02/php-and-long-running-processes.html

启动一个长时间运行的 PHP 进程。这在我的 Mac 上的 MAMP 配置中完美运行。然而,一旦我将它部署到我们的 VPS,我得到了一些非常奇怪的结果。

所以首先我做一个简单的测试,使用 SSH 连接:

echo '/usr/local/php53/bin/php -d memory_limit=512M -q /home/user/www/Update/Update.php;' | at now + 2minutes

结果:

warning: commands will be executed using /bin/sh
job 2300 at 2012-04-29 19:24

事实上,2 分钟后 php 脚本被执行。到目前为止,一切都很好。

接下来我尝试以下方法:

在我的浏览器中我打开:

www.myserver.com/Update/LaunchUpdates.php

此 php 脚本包含以下行:

exec("echo '/usr/local/php53/bin/php -d memory_limit=512M -q /home/user/www/Update/Update.php;' | at now + 2minutes");

发生的情况如下:我在 -l 处检查状态,我看到:

job 2304 at 2012-04-29 19:32

然后我等待 2 分钟并再次在 -l 处运行。我希望看到一个空的结果,但我得到:

job 2305 at 2012-04-29 19:34

2分钟后我得到

job 2306 at 2012-04-29 19:36

我对那里发生的事情一无所知。未执行 php 脚本,并且该作业似乎在 2 分钟后自行重新安排。这种情况一直持续到我完成工作为止。

有谁知道可能会发生什么?

更多信息:

cat /etc/*-release
Gentoo Base System version 1.6.14

更多细节。以下是 at 作业被调度时的内容:(at -c [ID])

#!/bin/sh
# atrun uid=1002 gid=100
# mail user 1
umask 33
SERVER_SIGNATURE=\<address\>Apache/2.2.20\ \(Unix\)\ mod_ssl/2.2.20\ OpenSSL/0.9.8o\ Server\ at\ xxx.yyyyy.com\ Port\ 80\</address\>"
"; export SERVER_SIGNATURE
HTTP_USER_AGENT=Mozilla/5.0\ \(Macintosh\;\ Intel\ Mac\ OS\ X\ 10_7_3\)\ AppleWebKit/534.55.3\ \(KHTML,\ like\ Gecko\)\ Version/5.1.5\ Safari/534.55.3; export HTTP_USER_AGENT
HTTP_HOST=xxx.yyyyy.com; export HTTP_HOST
SERVER_PORT=80; export SERVER_PORT
DOCUMENT_ROOT=/home/user/www; export DOCUMENT_ROOT
SCRIPT_FILENAME=/home/user/www/Update/LaunchUpdates.php; export SCRIPT_FILENAME
REQUEST_URI=/Update/LaunchUpdates.php; export REQUEST_URI
SCRIPT_NAME=/Update/LaunchUpdates.php; export SCRIPT_NAME
HTTP_CONNECTION=keep-alive; export HTTP_CONNECTION
REMOTE_PORT=36291; export REMOTE_PORT
PATH=/bin:/usr/bin; export PATH
PWD=/home/user/www/Update; export PWD
SERVER_ADMIN=webmaster@abcdef.com; export SERVER_ADMIN
REDIRECT_STATUS=200; export REDIRECT_STATUS
HTTP_ACCEPT_LANGUAGE=en-us; export HTTP_ACCEPT_LANGUAGE
HTTP_ACCEPT=text/html,application/xhtml+xml,application/xml\;q=0.9,\*/\*\;q=0.8; export HTTP_ACCEPT
REMOTE_ADDR=83.101.41.41; export REMOTE_ADDR
SHLVL=764; export SHLVL
SERVER_NAME=xxx.yyyyy.com; export SERVER_NAME
SERVER_SOFTWARE=Apache/2.2.20\ \(Unix\)\ mod_ssl/2.2.20\ OpenSSL/0.9.8o; export SERVER_SOFTWARE
QUERY_STRING=; export QUERY_STRING
SERVER_ADDR=1.2.3.4; export SERVER_ADDR
GATEWAY_INTERFACE=CGI/1.1; export GATEWAY_INTERFACE
SERVER_PROTOCOL=HTTP/1.1; export SERVER_PROTOCOL
HTTP_ACCEPT_ENCODING=gzip,\ deflate; export HTTP_ACCEPT_ENCODING
REQUEST_METHOD=GET; export REQUEST_METHOD
cd /home/user/www/Update || {
     echo 'Execution directory inaccessible' >&2
     exit 1
}
/usr/local/php53/bin/php -d memory_limit=512M -q /home/user/www/Update/Update.php;

在等待 2 分钟后重新安排工作时,我得到了新工作的内容,它是相同的,除了:

SHLVL=764 已经变成了 SHLVL=765

更多信息!

正如用户建议的那样,我尝试使用 nohup 而不是 at。所以我做了以下事情:

在 .sh 文件中生成要由 nohup 运行的命令(具有执行权限)。然后执行 exec('nohup .....')

我还在 LaunchUpdates 中添加了一个检查,以确保在 nohup 批处理完成运行之前不会再次调用它(我基本上是 rm .sh 文件及其批处理的结尾,在 LaunchUpdates 中我检查该文件是否存在)。

所以简而言之。

batchProcess.sh 包含:

/usr/local/php53/bin/php -d memory_limit=512M -q /home/user/www/Update/Update.php; 
rm /home/user/batchProcess.sh

我的 LaunchUpdates php 代码包含:

$batchFile = "/home/user/batchProcess.sh";

if (file_exists($batchFile))
{
    echo 'Process still running. Try again later!';
    exit;
}

exec('nohup /home/user/batchProcess.sh > ~/process.out 2> ~/process.err < /dev/null &');

不,会发生什么:

我在我的 php 脚本中注释掉了 exec 行,所以文件不会被执行而是生成。我通过使用 ssh 登录手动测试文件,更改为用户“user”并运行:

nohup /home/user/batchProcess.sh > ~/process.out 2> ~/process.err < /dev/null &

一切正常(最后删除 .sh 文件)!

接下来我取消注释 exec 行并重新运行 php 脚本。process.out 包含:

Process still running. Try again later!

这意味着它再次执行基本脚本而不是执行语句???我完全迷失在这里!由于我在两个帐户上运行相同的 bash 脚本,因此执行哪些命令不会出错。

我应该开始挖掘 apache 日志吗?

这应该需要一点时间,男孩是我错了......

4

9 回答 9

3

由于您示例中的“at”命令用于从调用终端取消绑定脚本,因此您可以使用“nohup”而不是“at”。

试试这个(从调用进程中解除绑定,等待 120 秒并调用 php):

/usr/bin/nohup /bin/sleep 120 2> /dev/null && \
/bin/date >> /tmp/longphp.log && \
/usr/local/php53/bin/php -d memory_limit=512M \
  -q /home/user/www/Update/Update.php >> /tmp/longphp.log 2>> /tmp/longphp.log

它创建 /tmp/longphp.log 文件进行分析。您还可以创建一个包含先前脚本的 shell 包装器,以简化操作。

于 2012-05-03T15:55:11.017 回答
2

第一次尝试:正如其他人所说,LaunchUpdates.php(您从浏览器调用)必须调用自身。它可能不会.../Update.php像您报告和打算那样调用,但是... /LaunchUpdates.php. 一个容易犯和忽视的错误,所以我的钱就在上面。[编辑:那不是问题。]

第二次尝试:现在您已经添加了生成的at脚本,我们可以看到您实际上调用了Update.php. 下一条线索:子shell 通常将变量SHLVL增加到比其父项的值大一。它超出个位数是非常不寻常的,在这种情况下,它表明您有超过 700 个 at 命令链,每个命令都由前一个启动。这排除了LaunchUpdates.php以某种方式通过 http 运行,因为SHLVL然后将被重置为比其在 apache 中的值大一。

我的新猜测:Update.php以某种方式执行$SCRIPT_NAMEor $SCRIPT_FILENAME,(正如我们在生成的脚本中看到的那样)设置为attoLaunchUpdates.php而不是Update.php. 您可以Update.php通过将其替换为空文件或仅将消息写入文件的存根来检查问题是否存在:问题应该消失。

最有可能的原因是那些环境设置。如果您无法弄清楚,请向我们展示代码Update.php(以简化形式,但确保问题仍然存在),以便我们大家看看。

编辑 2:所以您已经确认 LaunchUpdates.php 正在重新启动。由于它不调用自身,因此它必须由 调用Update.php

于 2012-05-06T09:05:53.830 回答
2

与其说是“at”问题的答案,但作为替代方案,您可以使用prggmr库设置超时以在 2 分钟内运行,该库将执行 Update.php 文件中包含的代码。

require 'path/to/prggmr/src/prggmr.php';

prggmr\timeout(function(){
    // Put logic from Update.php HERE
}, 120000);
// note the time is in milliseconds

prggmr\loop();

要运行代码,您将使用相同的命令,只需删除“at”

exec("echo '/usr/local/php53/bin/php -d memory_limit=512M -q /home/user/www/Update/Update.php;'");

这将在 2 分钟内在该超时时间内运行代码并在之后自动终止脚本,请注意,该库需要 PHP 5.4 才能运行,这只是一个替代方案,如果安装 5.4 不是一个选项,请忽略这一点。

于 2012-05-09T18:09:28.380 回答
2

除了使用“at -l”检查队列外,还可以尝试使用“at -c [ID]”查看AT将运行的实际命令。我认为这将有助于诊断错误是什么。

我高度怀疑 at 命令正在自行运行,因此它每 2 分钟重新安排一次。

于 2012-05-03T13:22:58.277 回答
2

文章作者编写了如下代码来实现调度:

print `echo /usr/bin/php -q longThing.php | at now`;

使用反引号使脚本显示调度命令的结果。at如果给出任何意外的输出,这可能会给您一个提示......也许添加2>&1以查看是否有任何错误。

于 2012-05-09T14:52:40.613 回答
2

我假设您在运行时以 root 身份登录(或者 uid PHP 在 webserver 中运行)at -l,并且作业与在 webserver 中运行的 uid PHP 相关联?

但是,其他人可能正在访问该网页,因为新作业的添加间隔与初始运行的延迟相同,这让我认为 Update.php 可能正在调用命令以再次运行自己?

于 2012-05-03T11:48:49.907 回答
2

如果您查看 /var/spool/at/atjobs,您会发现一个 .SEQ 和与此类似的文件

-rwx------ 1 sergio at   5077 may  3 17:53 a000010153c71d

该文件包含所有环境变量以及 atd 执行的命令。希望这可以帮助

于 2012-05-03T16:00:04.987 回答
1

您应该让您的脚本在现在 + 2 分钟内启动并创建和附加一个日志文件,例如:

file_put_contents(__FILE__.'.log', date('c') . "\n", FILE_APPEND);

然后你可以很容易地检查发​​生了什么。两分钟的差异让人们假设它会重新安排自己。

于 2012-05-03T15:35:47.543 回答
1

尝试这个:

exec('/home/user/batchProcess.sh >> ~/process.out 2>&1 | at now &');

于 2012-05-10T00:55:41.137 回答