Как определить, что скрипт PHP CLI находится в состоянии зависания - PullRequest
3 голосов
/ 26 августа 2011

Я использую supervisor (http://supervisord.org/), чтобы демонизировать довольно стандартный PHP-скрипт. Сценарий структурирован примерно так:

while (1) {
//  Do a SQL select
//  for any matching rows, do something
//  if I have been running for longer than 60 mins, exit
}

Сегодня этот скрипт (который был довольно стабильным в течение некоторого времени) завис. Он не аварийно завершился (т. Е. Подал сигналы SIGHUP или SIGTERM), которые бы предупредили супервизора о перезапуске процесса. Он не обнаружил никаких ошибок при обработке, которые могли бы быть обнаружены сценарием или, по крайней мере, вызвали фатальную ошибку и завершились. Вместо этих «ловимых» сценариев он просто сидел там. У нас есть настройка задания cron, которая запускается каждый час для перезапуска скрипта через ловушку supervisorctl, потому что, как представляется, общепринято, что PHP-скрипты имеют утечку с точки зрения памяти и могут быть перезапущены при длительном запуске. После этой перезагрузки сценарий возобновил работу в обычном режиме.

Мой вопрос: как я могу обнаружить, что этот скрипт завис? Я даже не могу начать диагностировать или устранить эту проблему, почему она зависла, если меня не предупредили об этом состоянии. Я ищу либо программное решение для этого, либо какой-то подход, который я могу использовать для самостоятельного создания решения (на PHP, Python, perl или shell).

Сценарий написан на PHP 5.2.6 и работает на обновленном сервере RHEL 5.

Пожалуйста, дайте мне знать, если я смогу поделиться какой-либо дополнительной информацией, если она поможет с более удивительным решением.

Спасибо!

Шахиб Р.

Ответы [ 3 ]

2 голосов
/ 26 августа 2011

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

Запись в журнал может быть записана в файл илибаза данных и должна содержать хотя бы индикатор состояния сценариев, например дату последнего изменения.Если этот скрипт не работает постоянно, то что-то также должно указывать на то, что он работает или остановился.В приведенном вами примере запись журнала будет происходить в цикле while хотя бы один раз, возможно, больше.Открытие указателей или соединения с БД требует времени и ресурсов, поэтому я рекомендую регистрировать только то, что необходимо.(Примечание. При использовании подхода с использованием текстового файла файл необходимо будет закрывать сразу после каждой записи.)

Пример:

while (1) {
    log('Running SQL select');
    //  Do a SQL select
    log('Results retrieved');
    //  for any matching rows, do something
    //  (check log) if I have been running for longer than 60 mins, exit
}

function log($msg) {
    // Write timestamp, $msg to log
}

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

Что касается памяти, если вы еще не используете mysql_free_result Вы должны дать ему попробовать.

1 голос
/ 26 августа 2011

Мое предложение будет похоже на то, что описал @Shroder, но сделаем это немного дальше. При каждом запуске вы создаете запись в журнале / базе данных, она будет помечена меткой времени + информация о транзакции (вы обновите транзакцию в начале выполнения до processing, а затем, когда закончите, подпишите запись с помощью completed.

На стороне вы запустите простую проверку cron и посмотрите, будет ли текущее время больше вашего триггера (60 минут и т. Д.), Используя отметку времени и состояние транзакции. В этот момент вы бросаете предупреждение и т.д .;

0 голосов
/ 26 августа 2011

Это довольно просто!Просто рассчитайте разницу во времени от начала цикла до текущей точки выполнения.

$starttime = microtime(true);
while (1) 
{
    //Do your stuff here
    //More SQL, whatever you need


    //Put this at the end of the loop
    $curtime = microtime(true);
    $timetaken = $curtime - $starttime;
    if($timetaken > (60 * 60))
    {
        break;
    }
}

microtime(true) вернет секунды, начиная с эпохи Unix, поэтому, если мы вычтемвремя, которое мы начинаем с текущего времени, мы берем / истекаем и выходим из цикла, если он превышает 60*60 секунд.

...