Вопрос по php – Как предотвратить выполнение задания cron, если оно уже запущено

23

У меня есть один скрипт php, и я выполняю этот скрипт через cron каждые 10 минут на CentOS.

Проблема в том, что если задание cron займет более 10 минут, то запустится другой экземпляр того же задания cron.

Я попробовал один трюк, а именно:

Created one lock file with php code (same like pid files) when the cron job started. Removed the lock file with php code when the job finished. And when any new cron job started execution of script, I checked if lock file exists and if so, aborted the script.

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

Можно ли как-нибудь снова остановить выполнение задания cron, если оно уже запущено, командами Linux или чем-то подобным?

Сделай сервис, который делает:forever() { do_stuff(); sleep_10_minutes(); } (псевдокод) Yetti99
(Изменить) Мое предложение было опубликовано в качестве решения только в том случае, если flock () не работает, я рекомендую сначала попробовать решение Джека. Я тестировал с помощьюflock() но по какой-то причине это не остановило несколько экземпляров cron-скрипта. Поэтому после небольшого поиска я нашел этот класс и немного изменил его для собственного использования: Abhinavsingh.com / блог / 2009/12 / ... h00ligan
@ Ответ Леонида Шагабутдинова должен быть принят как правильное решение этой проблемы. Dom

Ваш Ответ

9   ответов
44

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

$f = fopen('lock', 'w') or die ('Cannot create lock file');
if (flock($f, LOCK_EX | LOCK_NB)) {
    // yay
}

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

Если текущий скрипт преждевременно завершает работу, ОС блокирует любые блокировки файлов.

Спасибо Джеку за хороший ответ. Это лучшее решение. Но я просто беспокоюсь об этом, если cron остановился по какой-либо причине (может быть, сбой питания или перезагрузка системы) и не смог снять блокировку. Затем cron не запустится снова, потому что не может снова получить блокировку. Я думаю, что я должен попробовать это сначала:) Sanjay
Невозможно. ОС снимет блокировку независимо от того, что когда процесс остановится, для чего связанная статья h00ligan нуждается в некрасивом локализованном хакере. Ja͢ck
Это должен быть принятый ответ. Он работает именно так, как и предназначено для меня. Спасибо, Джек. Latheesan
12

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

$filehandle = fopen("lock.
if (flock($filehandle, LOCK_EX | LOCK_NB)) {
    // code here to start the cron job
   flock($filehandle, LOCK_UN);  // don't forget to release the lock
} else {
    // throw an exception here to stop the next cron job
}

fclose($filehandle);

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

if (flock($filehandle, LOCK_EX)) 
1

cronjoblock простая оболочка из 8 строк использует блокировку с помощью flock:

https: //gist.github.com/coderofsalvation/1102e56d3d4dcbb1e36

btw.cronjoblock также отменяет спам-рассылку cron: только выводить что-то, если что-то пойдет не так. Это удобно в отношении переменной MAILTO cron. Вывод stdout / stderr будет подавлен (поэтому cron не будет отправлять почту)е @, если данный процесс имеет код выхода> 0

0

Я использую это ::

<?php
// Create a PID file
if (is_file (dirname ($_SERVER['SCRIPT_NAME']) . "/.processing")) { die (); }
file_put_contents (dirname ($_SERVER['SCRIPT_NAME']) . "/.processing", "processing");

// SCRIPT CONTENTS GOES HERE //

@unlink (dirname ($_SERVER['SCRIPT_NAME']) . "/.processing");
?>
если по какой-либо причине ваш скрипт вылетает, cron больше никогда не запустится. Pooya
0

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

0
#!/bin/bash
ps -ef | grep -v grep | grep capture_12hz_sampling_track.php
if [ $? -eq 1 ];
then
     nohup /usr/local/bin/php /opt/Apache/htdocs/cmsmusic_v2/script/Mp3DownloadProcessMp4/capture_12hz_sampling_track.php &
else
      echo "Already running"
fi
0

Другая альтернатива:

<?php

/**
* Lock manager to ensure our cron doesn't run twice at the same time.
*
* Inspired by the lock mechanism in Mage_Index_Model_Process
*
* Usage:
* 
* $lock = Mage::getModel('stcore/cron_lock');
*
* if (!$lock->isLocked()) {
*      $lock->lock();
*      // Do your stuff
*      $lock->unlock();
* }
*/
class ST_Core_Model_Cron_Lock extends Varien_Object
{
    /**
     * Process lock properties
     */
    protected $_isLocked = null;
    protected $_lockFile = null;

    /**
     * Get lock file resource
     *
     * @return resource
     */
    protected function _getLockFile()
    {
        if ($this->_lockFile === null) {
            $varDir = Mage::getConfig()->getVarDir('locks');
            $file = $varDir . DS . 'stcore_cron.lock';
            if (is_file($file)) {
                $this->_lockFile = fopen($file, 'w');
            } else {
                $this->_lockFile = fopen($file, 'x');
            }
            fwrite($this->_lockFile, date('r'));
        }
        return $this->_lockFile;
    }

    /**
     * Lock process without blocking.
     * This method allow protect multiple process runing and fast lock validation.
     *
     * @return Mage_Index_Model_Process
     */
    public function lock()
    {
        $this->_isLocked = true;
        flock($this->_getLockFile(), LOCK_EX | LOCK_NB);
        return $this;
    }

    /**
     * Lock and block process.
     * If new instance of the process will try validate locking state
     * script will wait until process will be unlocked
     *
     * @return Mage_Index_Model_Process
     */
    public function lockAndBlock()
    {
        $this->_isLocked = true;
        flock($this->_getLockFile(), LOCK_EX);
        return $this;
    }

    /**
     * Unlock process
     *
     * @return Mage_Index_Model_Process
     */
    public function unlock()
    {
        $this->_isLocked = false;
        flock($this->_getLockFile(), LOCK_UN);
        return $this;
    }

    /**
     * Check if process is locked
     *
     * @return bool
     */
    public function isLocked()
    {
        if ($this->_isLocked !== null) {
            return $this->_isLocked;
        } else {
            $fp = $this->_getLockFile();
            if (flock($fp, LOCK_EX | LOCK_NB)) {
                flock($fp, LOCK_UN);
                return false;
            }
            return true;
        }
    }

    /**
     * Close file resource if it was opened
     */
    public function __destruct()
    {
        if ($this->_lockFile) {
            fclose($this->_lockFile);
        }
    }
}

Источник:https: //gist.github.com/wcurtis/953917

0

который специально занимался отправкой текстовых сообщений с использованием существующего API. На моем локальном ящике работа cron работала нормально, но на ящике моего клиента отправляла двойные сообщения. Хотя это не имеет смысла для меня, я дважды проверил разрешения для папки, отвечающей за отправку сообщений, и было установлено разрешение root. Как только я установил владельца как www-data (Ubuntu), он начал вести себя нормально.

Это может быть не проблема для вас, но если это простой скрипт cron, я бы дважды проверил разрешения.

Похожие вопросы