Автоматический перезапуск PHP скрипта при выходе

есть ли способ, которым я могу автоматически перезапустить PHP-скрипт всякий раз, когда он выходит, независимо от того, был ли он правильно завершен или завершен из-за ошибки или максимального использования памяти и т. д.?

7 ответов


PHP-скрипт также может перезапустить себя с помощью PCNTL.

отказ от ответственности: смысл этого упражнения только в том, чтобы доказать, что PHP вполне способен перезапустить себя и ответить на вопрос:

есть ли способ, которым я могу автоматически перезапустить PHP-скрипт всякий раз, когда он выходит, независимо от того, был ли он правильно завершен или завершен из-за ошибки?

поэтому мы не можем вдаваться в подробности о unix процессы, поэтому я бы предложил вам начать с книги PCNTL или обратитесь к php-pcntl для получения более подробной информации.

в примерах мы предположим, что это скрипт php CLI, запущенный в среде *nix из терминала с полу-приличной оболочкой с помощью команды:

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

мы передаем атрибут restart count как индикатор того, что процесс был перезапущен.

могу ли я автоматически перезапустить 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 в обработчике ошибок, иначе 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  
    }

это, безусловно, может быть приключение, преследуя процессы, которые перезапускают иммунитет к вашему убийству, если вам удастся поймать их в процессе. =)

убедитесь, что сценарий имеет достаточно, чтобы сделать так, что у вас может быть достаточно времени, чтобы убить его.

попробуй:

$ kill -9

или:

$ kill -s KILL

настаивать мертвецы остаются мертвыми. Это не праздная ссылка, поскольку вам было бы разумно рассматривать зомби и сирот, но в конечном итоге даже самые сложные процессы обычно все еще нуждаются в родителе, поэтому убийство сеанса оболочки должно видеть их на отдых.

применяются все стандартные оговорки, удачи! =)


предположим:

  • вызовите тот же файл сценария x количество раз
  • повторный вызов, если он будет прерван/завершен / остановлен
  • автоматизированный процесс для бесконечного цикла

некоторые варианты приходят на ум, чтобы достичь своей цели [решения на основе Unix / Linux]:

  1. использовать баш скрипт, так что скрипт PHP после остановки / завершения / прерывания может быть возобновил:

    #!/bin/bash
    clear
    date
    
    php -f my_php_file.php
    sleep 100
    # rerun myself
    exec 
    
  2. Контроллер Жира обработчик выполнения, это ключевые функции, которые вы ищете:

    • используется для многократного запуска других программ
    • может запускать множество экземпляров любого скрипта / программы параллельно
    • любой скрипт может быть перезапущен после завершения выполнения

    очень похож на 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, выходы.


использование 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

дополнить ow3nответ, скрипт php CLI похож на любую другую команду и может быть легко повторен в бесконечном while петли. Он будет работать точно так же, как запуск команды сам по себе, но будет работать снова, когда он закончится. Нажмите Ctrl+C когда вы хотите завершить цикл.

Edit: если ваш процесс ловит сигналы (что должно быть, если это 12 фактор 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. использование нескольких процессов вместо нескольких потоки могут показаться неэффективными, но поверьте мне, это более чем компенсирует простоту, ремонтопригодность, простоту отладки и т. д. Многопоточность может быстро привести вас к поддержание АД, что объясняет появление Актер Модель.
  3. эффективность бизнес-требование? Обычно это связано с ограниченными ресурсами (например, встроенными устройствами), но я сомневаюсь, что это ваш случай, иначе вы не использовали бы PHP. Считаю, что в наше время, люди много дороже, чем компьютеры. Поэтому, чтобы минимизировать затраты, гораздо выгоднее максимизировать эффективность ваших людей, а не ваших компьютеров (см. правило экономики философии Unix). Многопоточность, если она будет выполнена хорошо, сэкономит вам немного денег, оптимизируя использование ваших компьютеров, но будет стоить вам гораздо больше с точки зрения головной боли, которую это вызовет для ваших людей.
  4. если вы абсолютно должны использовать я настоятельно призываю вас рассмотреть актерскую модель. Акка это отличная реализация.

о процессах:

  1. если вы используете свои скрипты в разработке, одного процесса [каждого типа] должно быть достаточно.
  2. если вы используете свои сценарии в производстве, вы должны полагаться на менеджера процессов для управления процессами, такими как выскочка.
  3. избегайте превращения процессов в Димоны.
  4. узнать об 12 фактор применения, особенно процессы, параллелизм & одноразовость.

обновление: Тара сделать запуск и управление флотом процессов еще проще.


перезапустить скрипт каждый раз его остановки можно обернуть 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

только мои 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($_);
}