退出时自动重启PHP脚本

时间:2012-03-21 04:02:04

标签: php

有没有一种方法可以在退出时自动重启PHP脚本,无论它是否已正确退出,或因错误或内存使用量最大而终止等等?

8 个答案:

答案 0 :(得分:46)

PHP脚本也可以通过PCNTL重新启动。

免责声明:本练习的目的只是为了证明PHP能够完全重启自己并回答这个问题:

  

有没有办法可以在退出时自动重启PHP脚本,无论它是否已正确退出,或因错误而终止?

因此,我们可以深入了解有关unix进程的任何细节,因此我建议您从PCNTL book开始,或参考了解更多详情。

在示例中,我们假设它是一个在* nix环境中从具有半正式shell的终端使用以下命令启动的PHP CLI脚本:

$ php i-can-restart-myself.php 0

我们传递一个重启计数属性作为重启过程的指示。

我可以自动重启PHP脚本吗?

是的,我可以!

<?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);

但是你知道我想要更多的东西吗?

当然可以!我可以重新启动kill,hub甚至Ctrl-C!

<?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)

<强>假设:

  • 调用相同的脚本文件x次
  • 如果中断/结束/停止则重新呼叫
  • 无限循环的自动化过程

实现您的目标[Unix / Linux解决方案]时会想到一些选项:

  1. 使用BASH script,以便可以恢复停止/结束/中断后的PHP脚本:

    #!/bin/bash
    clear
    date
    
    php -f my_php_file.php
    sleep 100
    # rerun myself
    exec $0
    
  2. The Fat Controller执行处理程序,这是您正在寻找的主要功能:

    • 用于重复运行其他程序
    • 可以并行运行任何脚本/程序的多个实例
    • 任何脚本在完成执行后都可以重新运行

    与CRON非常相似,但这些关键功能正是您所需要的

  3. 使用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

那就是说,我对你评论中的含义有点担心:

关于主题:

  1. 我会考虑在PHP中使用线程。使用该模型的语言具有比PHP更好/稳定/成熟的环境(例如Java)。
  2. 使用多个进程而不是多个线程可能看起来效率低下,但是相信我,它在简单性,可维护性,易于调试等方面做得更多。多线程可以快速引导您maintenance hell,解释了Actor Model
  3. 的出现
  4. 效率是业务要求吗?这通常是由于有限的资源(像嵌入式设备之类的东西),但我怀疑这是你的情况,否则你不会使用PHP。现在考虑一下,people are much more expensive than computers。因此,为了最大限度地降低成本,最有效的方法是最大限度地提高人员的效率,而不是计算机的效率(see the Unix philosophy's Rule of Economy)。如果做得好,多线程可以通过优化计算机的使用来节省一些钱,但是会给你的员工带来更多的麻烦。
  5. 如果你绝对必须使用线程,我强烈建议你考虑一下Actor模型。 Akka是一个很棒的实现。
  6. 关于流程:

    1. 如果您正在开发中运行脚本,[每种类型]的一个进程就足够了。
    2. 如果您正在生产中运行脚本,则应依靠流程管理器来管理流程,例如Upstart
    3. 避免将您的流程变为守护。
    4. 了解12 factor applications,尤其是processesconcurrency&amp; disposability
    5. 更新: 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__);