Не запускайте задачу cron php, пока последняя не будет завершена

2

У меня есть скрипт php-cli который запускается cron каждые 5 minutes. Поскольку этот интервал является коротким, multiple processes выполняются multiple processes. Это не то, что я хочу, так как этот скрипт должен писать внутри текстового файла числовой id который увеличивается каждый раз. Бывает, что writers пишут в этом текстовом файле, а записанное значение неверно.

Я попытался использовать функцию php flock чтобы заблокировать запись в файле, когда на него записывается другой процесс, но он не работает.

$fw = fopen($path, 'r+');
if (flock($fw, LOCK_EX)) {
    ftruncate($fw, 0);
    fwrite($fw, $latestid);
    fflush($fw);
    flock($fw, LOCK_UN);
}
fclose($fw);

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

  • 0
    Сценарий работает более 5 минут?
  • 0
    Просто чтобы быть ясно, другой процесс также использует flock, правильно?
Показать ещё 2 комментария
Теги:
cron
concurrency

5 ответов

3
Лучший ответ

Решение, которое я использую с помощью bash script таково:

exec 9>/path/to/lock/file
if ! flock -n 9  ; then
    echo "another instance is running";
    exit 1
fi
# this now runs under the lock until 9 is closed (it will be closed automatically when the script ends)  

В файле /var/lock/file создается файловый дескриптор 9>, и flock завершит выполнение нового процесса, который будет запущен, если нет другого экземпляра скрипта, который работает.

Как я могу гарантировать, что одновременно запускается только один экземпляр скрипта (взаимное исключение)?

2

Я действительно не понимаю, как увеличение счетчика каждые 5 минут приведет к нескольким процессам, которые пытаются написать файл счетчика одновременно, но...

Гораздо проще использовать простой механизм блокировки, подобный приведенному ниже:

<?php

$lock_filename = 'nobodyshouldincrementthecounterwhenthisfileishere';

if(file_exists($lock_filename)) {
  return;
}

touch($lock_filename);

// your stuff...

unlink($lock_filename);

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

Более сложные подходы также возможны, как вы предлагаете, например, fork job в своем собственном процессе, записывать PID в файл, а затем перед запуском задания можно проверить, продолжает ли этот PID.

  • 0
    Увеличение не является проблемой, когда существует только один процесс, который увеличивает его одновременно. Но, как я уже сказал, в этом скрипте есть другие операции, которые заставляют его работать более 5 минут (сканирование и вставка).
  • 0
    Что ж, в этом случае, если вы не хотите, чтобы какой-либо из этих процессов пропустил приращение, вместо простого return когда файл блокировки существует, идеальным способом будет держать его в цикле, который sleep в течение секунды или около того, а затем перепроверяет блокировку. файл.
Показать ещё 2 комментария
0

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

 ps -ef|grep <process_name>|grep -v grep|wc -l

и проверьте, будет ли его результат равен 0. Только в этом случае ваша программа может быть запущена. Я полагаю, что вы должны гарантировать отсутствие стороннего процесса с похожим именем. (Для этого дайте вашей программе более длинное и уникальное имя). И имя вашей программы не должно содержать шаблон "grep".

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

 <time_specification>  <your_starter_script>  <your_program> ...

2 важных замечания: код выхода вашего_starter_script должен быть 0 в случае не запуска вашей программы, и было бы лучше полностью запретить запись в stdout или stderr с помощью этого скрипта.

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

0

Или, может быть, даже проще, чем мой предыдущий ответ (используя at запланировать сценарий снова запустить в течение 5 минут), это сделать ваш сценарий демона, используя не-завершающий цикл, например, так:

while(1) {
  // whatever your script does here....
  sleep(300) //wait 5 minutes
}

Затем, вы можете избавиться от планирования путем cron или at целом. Просто запустите свой сценарий в фоновом режиме из командной строки, например:

/path/to/your/script &

Или добавьте /path/to/your/script в /etc/rc.local, чтобы ваш скрипт запускался автоматически при загрузке машины.

0

Вместо того, чтобы использовать cron для запуска сценария каждых 5 минут, как об использовании at запланировать сценарий для повторного запуска, через 5 минут после его завершения. В конце вашего сценария вы можете использовать shell_exec() для запуска команды at чтобы запланировать запуск скрипта за 5 минут, например:

at now + 5 minutes /path/to/script
  • 0
    Такое решение с использованием «at» не является надежным. Если однажды что-то пойдет не так, например, процесс был остановлен или временная проблема с механизмом планирования UNIX Linux (перегрузка), то цепь «реинкарнаций» будет разорвана, так что вам нужно организовать «няню», то есть нечто большее, чем простой мониторинг, который следит за цепью и перезапускает ее, если она была сломана.
  • 0
    это не очень эффективно, задача cron - планировать процессы (вам не нужно изобретать велосипед).

Ещё вопросы

Сообщество Overcoder
Наверх
Меню