Как определить, работает ли PHP скрипт?

У меня есть cron script, который выполняет PHP скрипт каждые 10 минут. script проверяет очередь и обрабатывает данные в очереди. Иногда очередь имеет достаточно данных, чтобы прослужить более 10 минут обработки, создавая потенциал двух сценариев, пытающихся получить доступ к тем же данным. Я хочу узнать, запущен ли script, чтобы предотвратить запуск нескольких копий script. Я подумал о создании флага базы данных, в котором говорится, что обработка script обрабатывается, но если script когда-либо выходил из строя, он оставил бы его в положительном состоянии. Есть ли простой способ узнать, работает ли PHP скрипт с помощью PHP или оболочки script?

Ответ 1

Если вам нужно, чтобы он был абсолютно аварийным, вы должны использовать семафоры, которые автоматически выпускаются, когда php завершает обработку конкретного запроса.

Более простым подходом было бы создать запись БД или файл в начале выполнения и удалить его в конце. Вы всегда можете проверить "возраст" этой записи/файла, и если он старше, чем в 3 раза больше обычного выполнения script, предположим, что он разбился и удалил его.

Там нет "серебряной пули", это просто зависит от ваших потребностей.

Ответ 2

Вы можете просто использовать файл блокировки. PHP flock() функция предоставляет простую оболочку для Unix flock, которая обеспечивает консультативные блокировки файлов.

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

Вы также можете следовать свободному соглашению Unix о том, чтобы сделать ваш файл блокировки "PID файлом", то есть после получения блокировки файла, ваш script напишет на него свой PID. Даже если вы никогда не читали это из своего script, вам будет удобно, если ваш script когда-либо зависает или сходит с ума, и вы хотите найти его PID, чтобы вручную его убить.

Здесь выполняется копирование/вставка:

#!/usr/bin/php
<?php

$lock_file = fopen('path/to/yourlock.pid', 'c');
$got_lock = flock($lock_file, LOCK_EX | LOCK_NB, $wouldblock);
if ($lock_file === false || (!$got_lock && !$wouldblock)) {
    throw new Exception(
        "Unexpected error opening or locking lock file. Perhaps you " .
        "don't  have permission to write to the lock file or its " .
        "containing directory?"
    );
}
else if (!$got_lock && $wouldblock) {
    exit("Another instance is already running; terminating.\n");
}

// Lock acquired; let write our PID to the lock file for the convenience
// of humans who may wish to terminate the script.
ftruncate($lock_file, 0);
fwrite($lock_file, getmypid() . "\n");

/*
    The main body of your script goes here.
*/
echo "Hello, world!";

// All done; we blank the PID file and explicitly release the lock 
// (although this should be unnecessary) before terminating.
ftruncate($lock_file, 0);
flock($lock_file, LOCK_UN);

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

Ответ 3

Если вы используете Linux, это должно работать в верхней части вашего script:

$running = exec("ps aux|grep ". basename(__FILE__) ."|grep -v grep|wc -l");
if($running > 1) {
   exit;
}

Ответ 4

Обычный способ для демонов * nix (хотя и не обязательно PHP-скриптов, но он будет работать) заключается в использовании файла .pid.

Когда script начинает проверку наличия файла .pid с именем script (обычно хранится в /var/run/ ). Если он не существует, создайте его, установив его содержимое в PID процесса, запускающего script (используя getmypid), затем продолжите с нормальным исполнением. Если он существует, прочитайте PID из него и посмотрите, работает ли этот процесс, возможно, запустив ps $pid. Если он запущен, выйдите. В противном случае перезапишите его содержимое с помощью PID (как указано выше) и продолжите нормальное выполнение.

Когда выполнение закончено, удалите файл.

Ответ 5

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

Я использую его для постоянной работы системы мониторинга. Задача cron запускает script каждые 5 минут, но если другой не остановился по какой-либо причине (обычно, если он разбился, что очень редко!), Новый будет просто выйти из себя.

// The file to store our process file
define('PROCESS_FILE', 'process.pid');

// Check I am running from the command line
if (PHP_SAPI != 'cli') {
    log_message('Run me from the command line');
    exit;
}

// Check if I'm already running and kill myself off if I am
$pid_running = false;
if (file_exists(PROCESS_FILE)) {
    $data = file(PROCESS_FILE);
    foreach ($data as $pid) {
        $pid = (int)$pid;
        if ($pid > 0 && file_exists('/proc/' . $pid)) {
            $pid_running = $pid;
            break;
        }
    }
}
if ($pid_running && $pid_running != getmypid()) {
    if (file_exists(PROCESS_FILE)) {
        file_put_contents(PROCESS_FILE, $pid);
    }
    log_message('I am already running as pid ' . $pid . ' so stopping now');
    exit;
} else {
    // Make sure file has just me in it
    file_put_contents(PROCESS_FILE, getmypid());
    log_message('Written pid with id '.getmypid());
}

Он не будет работать без изменений в Windows, но должен быть в порядке в системах на основе UNIX.

Ответ 6

Вы можете использовать новый Symfony 2.6 LockHandler

$lock = new LockHandler('update:contents');
if (!$lock->lock()) {
    echo 'The command is already running in another process.';
}

Ответ 7

Это сработало для меня. Установите запись базы данных с флагом блокировки и меткой времени. Мой script должен завершиться в течение 15 минут, поэтому добавил, что в качестве последнего заблокированного feild для проверки:

       $lockresult = mysql_query("
       SELECT *
       FROM queue_locks
       WHERE `lastlocked` > DATE_SUB(NOW() , INTERVAL 15 MINUTE)    
       AND `locked` = 'yes'
       AND `queid` = '1'
       LIMIT 1
        "); 
$LockedRowCount = mysql_num_rows($lockresult);

if($LockedRowCount>0){
    echo "this script is locked, try again later";
    exit;   
}else{
//Set the DB record to locked and carry on son
$result = mysql_query("
            UPDATE `queue_locks` SET `locked` = 'yes', `lastlocked` = CURRENT_TIMESTAMP WHERE `queid` = 1;
            ");

}

Затем откройте его в конце script:

    $result = mysql_query("UPDATE `queue_locks` SET `locked` = 'no' WHERE `queid` = 1;");

Ответ 8

Я пробовал это. Хорошо работает и в Windows.

    $status_file=__DIR__.'/mail_process.txt';
    if(file_exists($status_file) && file_get_contents($status_file)=='running'){
                echo 'Program running';
            exit;
    }
    file_put_contents($status_file,'running');

       // Code block
       //...........

    file_put_contents($status_file,'finished');

Благодаря @martinodf для его идеи.

Ответ 9

Предполагая, что это сервер Linux, и у вас есть cronjobs доступны

///Check for running script and run if non-exist///

#! /bin/bash

check=$(ps -fea | grep -v grep | grep script.php | wc -l)
date=$(date +%Y-%m%d" "%H:%M:%S)
if [ "$check" -lt 1 ]; then
echo "["$date"] Starting script" >> /path/to/script/log/
/sbin/script  ///Call the script here - see below///
fi

файл сценария

#/usr/bin/php /path/to/your/php/script.php

Ответ 10

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

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

Если вы уверены, что script должен выполняться только один раз, то относительно легко проверить из script, запущен ли он, когда вы его запускаете. Вот код:

function checkrun() {
    exec("ps auxww",$ps);
    $r = 0;
    foreach ($ps as $p) {
        if (strpos($p,basename(__FILE__))) {
            $r++;
            if ($r > 1) {
                echo "too many instances, exiting\n";
                exit();
            }
        }
    }
}

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

Потенциал здесь: Я предполагаю, что у вас никогда не будет двух сценариев с одним и тем же базовым именем, которое может законно работать одновременно (например, тот же script работает под двумя разными пользователями). Если это возможно, вам нужно расширить проверку на нечто более сложное, чем простая подстрока в файле basename. Но это работает достаточно хорошо, если у вас есть уникальные имена файлов для ваших скриптов.

Ответ 11

Просто добавьте следующее в начало script.

<?php
    // Ensures single instance of script run at a time.
    $fileName = basename(__FILE__);
    $output = shell_exec("ps -ef | grep -v grep | grep $fileName | wc -l");
    //echo $output; 
    if ($output > 2)
    {
        echo "Already running - $fileName\n";
        exit;   
    }

    // Your PHP скрипт code.
?>