有没有一种方法可以在退出时自动重启PHP脚本,无论它是否已正确退出,或因错误或内存使用量最大而终止等等?
答案 0 :(得分:46)
免责声明:本练习的目的只是为了证明PHP能够完全重启自己并回答这个问题:
有没有办法可以在退出时自动重启PHP脚本,无论它是否已正确退出,或因错误而终止?
因此,我们可以深入了解有关unix进程的任何细节,因此我建议您从PCNTL book开始,或参考php-pcntl了解更多详情。
在示例中,我们假设它是一个在* nix环境中从具有半正式shell的终端使用以下命令启动的PHP CLI脚本:
$ php i-can-restart-myself.php 0
我们传递一个重启计数属性作为重启过程的指示。
<?php
echo ++$argv[1]; // count every restart
$_ = $_SERVER['_']; // or full path to php binary
echo "\n======== start =========\n";
// do a lot of stuff
$cnt = 0;
while( $cnt++ < 10000000 ){}
echo "\n========== end =========\n";
// restart myself
pcntl_exec($_, $argv);
<?php
echo ++$argv[1];
$_ = $_SERVER['_'];
register_shutdown_function(function () {
global $_, $argv; // note we need to reference globals inside a function
// restart myself
pcntl_exec($_, $argv);
});
echo "\n======== start =========\n";
// do a lot of stuff
$cnt = 0;
while( $cnt++ < 10000000 ){}
echo "\n========== end =========\n";
die; // exited properly
// we can't reach here
pcntl_exec($_, $argv);
<?php
echo ++$argv[1];
$_ = $_SERVER['_'];
register_shutdown_function(function () {
global $_, $argv;
// restart myself
pcntl_exec($_, $argv);
});
echo "\n======== start =========\n";
// do a lot of stuff
$cnt = 0;
while( $cnt++ < 10000000 ){}
echo "\n===== what if? =========\n";
require 'OOPS! I dont exist.'; // FATAL Error:
// we can't reach here
echo "\n========== end =========\n";
die; // exited properly
// we can't reach here
pcntl_exec($_, $argv);
<?php
echo ++$argv[1];
$_ = $_SERVER['_'];
$restartMyself = function () {
global $_, $argv;
pcntl_exec($_, $argv);
};
register_shutdown_function($restartMyself);
pcntl_signal(SIGTERM, $restartMyself); // kill
pcntl_signal(SIGHUP, $restartMyself); // kill -s HUP or kill -1
pcntl_signal(SIGINT, $restartMyself); // Ctrl-C
echo "\n======== start =========\n";
// do a lot of stuff
$cnt = 0;
while( $cnt++ < 10000000 ){}
echo "\n===== what if? =========\n";
require 'OOPS! I dont exist.'; // FATAL Error:
// we can't reach here
echo "\n========== end =========\n";
die; // exited properly
// we can't reach here
pcntl_exec($_, $argv);
我现在如何终止它?
如果你通过按住Ctrl-C来淹没这个过程,你可能只是在关机的某个地方捕获它。
<?php
echo ++$argv[1];
$_ = $_SERVER['_'];
$restartMyself = function () {
global $_, $argv;
pcntl_exec($_, $argv);
};
register_shutdown_function($restartMyself);
pcntl_signal(SIGTERM, $restartMyself);
pcntl_signal(SIGHUP, $restartMyself);
pcntl_signal(SIGINT, $restartMyself);
set_error_handler($restartMyself , E_ALL); // Catch all errors
echo "\n======== start =========\n";
// do a lot of stuff
$cnt = 0;
while( $cnt++ < 10000000 ){}
echo $CAREFUL_NOW; // NOTICE: will also be caught
// we would normally still go here
echo "\n===== what if? =========\n";
require 'OOPS! I dont exist.'; // FATAL Error:
// we can't reach here
echo "\n========== end =========\n";
die; // exited properly
// we can't reach here
pcntl_exec($_, $argv);
虽然乍一看这似乎工作得很好,因为pcntl_exec在同一个进程中运行,我们没有注意到事情是非常错误的。如果你想生成一个新进程并让旧进程死掉,这是一个非常可行的替代方案,请参阅下一篇文章,你会发现每次迭代都会启动2个进程,因为我们会因为共同的疏忽而触发PHP注意和错误。当然,通过在错误处理程序中调用pcntl_exec之后确保我们die()或exit()可以很容易地纠正这一点,否则PHP假定容差已被接受并继续。
我想说的是,即使您能够重新启动失败的脚本,这也不是允许破坏代码的借口。虽然这种做法可能存在可行的用例,但 我强烈反对失败时重新启动应该被用作脚本因错误而失败的解决方案! 正如我们所看到的那样这些例子没有办法确切知道失败的地方,所以我们无法确定它将从何处开始。寻求可能看起来工作正常的“快速修复”解决方案可能会遇到更多问题。
我宁愿看到通过一些适当的单元测试解决缺陷,这将有助于清除罪魁祸首,以便我们可以纠正问题。如果PHP内存不足,可以通过在使用后取消设置变量来保守使用资源来避免。 (顺便说一句,你会发现分配null比使用unset对同样的结果快得多)我担心,如果没有解决,重启肯定会成为你已经拥有的蠕虫病毒的合适开启者。
答案 1 :(得分:8)
要启动单独的进程,请将restartMyself函数更改为:
<?php
$restartMyself = function () {
global $_, $argv;
if(!pcntl_fork()) // We only care about the child fork
pcntl_exec($_, $argv);
die; // insist that we don't tolerate errors
}
这肯定是一次冒险,如果你设法在进程中捕获它们,那么追逐可以重新开始杀戮的进程。 =)
确保脚本有足够的时间来执行此操作,您可能有足够的时间来终止它。
尝试:
$ kill -9
或:
$ kill -s KILL
坚持不死生物死了。这不是空闲参考,因为你最好考虑僵尸和孤儿,但最终即使是最棘手的进程通常仍需要一个父进程,所以杀死shell会话应该让他们休息。
所有标准免责声明适用,祝您好运! =)
答案 2 :(得分:5)
<强>假设:强>
实现您的目标[Unix / Linux解决方案]时会想到一些选项:
使用BASH script,以便可以恢复停止/结束/中断后的PHP脚本:
#!/bin/bash
clear
date
php -f my_php_file.php
sleep 100
# rerun myself
exec $0
The Fat Controller执行处理程序,这是您正在寻找的主要功能:
与CRON非常相似,但这些关键功能正是您所需要的
使用CRON,您可以让它以例如一分钟的间隔调用您的PHP脚本,然后在PHP脚本的开头使用此代码,您将能够控制正在运行的线程数,如果8已在运行,则退出:
exec('ps -A | grep ' . escapeshellarg(basename(__FILE__)) , $results);
if (count($results) > 7) {
echo "8 Already Running\n"
die(0);
}
搜索使用当前文件的相同路径运行的进程,并返回找到的进程数。然后,如果他们更大7,退出。
答案 3 :(得分:1)
使用CRON
#!/bin/sh
process = 'script.php'
if ps ax | grep -v grep | grep $process
then
echo "$process is running..."
else
echo "$process not running, relaunching..."
php /your/php/script.php
fi
答案 4 :(得分:1)
为了补充ow3n的答案,PHP CLI脚本就像任何其他命令一样,并且可以在无限while
循环中轻松重复。它将与单独运行命令完全相同,但在结束时将再次运行。如果要终止循环,只需按 Ctrl + C 。
修改: 如果您的流程catches signals(如果它是12 factor app则应该如此),您将需要按住 Ctrl + C 而不是按下它。进程将捕获一个按下(它应该自行终止),但while
循环将再次重新启动进程(如果您只是想重新启动进程而不是终止它,这将非常有用)。如果您的进程没有捕获信号,则 Ctrl + C 将同时终止进程和while
循环。
这是最简单的方法,没有任何额外的输出:
while :; do [command]; done
while :; do echo "test" && sleep 1; done
while :; do php myscript.php; done
那就是说,我对你评论中的含义有点担心:
关于主题:
关于流程:
更新: Containers让运行&amp;更轻松地管理一系列流程。
答案 5 :(得分:0)
要在每次停止运行时重新启动脚本,您可以将user1179181的脚本循环包装。
#!/bin/bash
while true
do
if ps ax | grep -v grep | grep "script.php"
then
echo "process is running..."
else
clear
date
echo "process not running, relaunching..."
php script.php
fi
sleep 10
done
答案 6 :(得分:0)
只需5美分。 PCNTL重启并自定义错误处理..
register_shutdown_function("shutdownHandler");
function shutdownHandler() //will be called when php script ends.
{
$lasterror = error_get_last();
switch ($lasterror['type'])
{
case E_ERROR:
case E_CORE_ERROR:
case E_COMPILE_ERROR:
case E_USER_ERROR:
case E_RECOVERABLE_ERROR:
case E_CORE_WARNING:
case E_COMPILE_WARNING:
case E_PARSE:
$error = "[SHUTDOWN] lvl:" . $lasterror['type'] . " | msg:" . $lasterror['message'] . " | file:" . $lasterror['file'] . " | ln:" . $lasterror['line'];
myCustomLogHandler("FATAL EXIT $error\n");
}
$_ = $_SERVER['_'];
global $_;
pcntl_exec($_);
}
答案 7 :(得分:0)
只需执行以下操作:
sleep(1);
system('php '.__FILE__);