nic*_*kl- 49
免责声明:本练习的目的只是为了证明PHP完全能够重启自己并回答这个问题:
有没有一种方法可以在退出时自动重启PHP脚本,无论它是否已正确退出,或因错误而终止?
因此,我们可以深入了解有关unix进程的任何细节,因此我建议您从PCNTL本书开始,或参考php-pcntl了解更多详细信息.
在这些示例中,我们假设它是一个在*nix环境中从具有半正式shell的终端使用以下命令启动的PHP CLI脚本:
$ php i-can-restart-myself.php 0
Run Code Online (Sandbox Code Playgroud)
我们传递一个重启计数属性作为重新启动进程的指示器.
<?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);
Run Code Online (Sandbox Code Playgroud)
<?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);
Run Code Online (Sandbox Code Playgroud)
<?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);
Run Code Online (Sandbox Code Playgroud)
<?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);
Run Code Online (Sandbox Code Playgroud)
我现在如何终止它?
如果通过按住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);
Run Code Online (Sandbox Code Playgroud)
虽然乍一看似乎工作正常,但因为pcntl_exec在同一个进程中运行,所以我们没有注意到事情是非常错误的.如果你想生成一个新进程并让旧进程死掉,这是一个非常可行的替代方案,请参阅下一篇文章,你会发现每次迭代都会启动2个进程,因为我们会因为共同的疏忽而触发PHP注意和错误.当然,通过在错误处理程序中调用pcntl_exec之后确保我们die()或exit()可以很容易地纠正这一点,否则PHP假定容差已被接受并继续.
我想说的是,即使您能够重新启动失败的脚本,这也不是允许破坏代码的借口.虽然这种做法可能存在可行的用例,但我强烈反对应该采用失败重启作为脚本因错误而失败的解决方案!正如我们从这些例子中看到的那样,没有办法确切地知道它失败了,所以我们无法确定它将从何处开始.寻求可能看起来工作正常的"快速修复"解决方案现在可能会遇到更多问题.
我宁愿看到通过一些适当的单元测试来解决缺陷,这将有助于清除罪魁祸首,以便我们可以纠正问题.如果PHP内存不足,可以通过在使用后取消设置变量来保守使用资源来避免.(顺便说一句,你会发现分配null比使用unset对同样的结果快得多)我担心,如果没有解决,重启肯定会成为你已经拥有的蠕虫的合适开启者.
要启动单独的进程,请将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
}
Run Code Online (Sandbox Code Playgroud)
这肯定是一次冒险,如果你设法在进程中捕获它们,那么追逐可以重新开始杀戮的进程.=)
确保脚本有足够的时间来执行此操作,您可能有足够的时间来终止它.
尝试:
$ kill -9
Run Code Online (Sandbox Code Playgroud)
要么:
$ kill -s KILL
Run Code Online (Sandbox Code Playgroud)
坚持不死生物死了.这不是空闲参考,因为你最好考虑僵尸和孤儿,但最终即使是最棘手的进程通常仍然需要一个父级,所以杀死shell会话应该让他们休息.
所有标准免责声明适用,祝你好运!=)
假设:
为实现您的目标[Unix/Linux解决方案],我们会想到一些选项:
使用BASH脚本,以便可以恢复停止/结束/中断后的PHP脚本:
#!/bin/bash
clear
date
php -f my_php_file.php
sleep 100
# rerun myself
exec $0
Run Code Online (Sandbox Code Playgroud)胖控制器执行处理程序,这是您正在寻找的关键功能:
与CRON非常相似,但这些关键功能正是您所需要的
使用CRON,你可以让它以例如一分钟的间隔调用你的PHP脚本,然后在PHP脚本的开始使用这段代码,你将能够控制运行的线程数,如果8已经存在则退出运行:
exec('ps -A | grep ' . escapeshellarg(basename(__FILE__)) , $results);
if (count($results) > 7) {
echo "8 Already Running\n"
die(0);
}
Run Code Online (Sandbox Code Playgroud)
搜索使用当前文件的相同路径运行的进程,并返回找到的进程数.然后,如果他们更大7,退出.