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


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

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

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

  1. Создал один файл блокировки с php-кодом (такой же, как pid-файлы) при запуске задания cron.
  2. Удалил файл блокировки с кодом php, когда работа была завершена.
  3. И когда начиналось какое-либо новое задание cron выполняя скрипт, я проверил , существует ли файл блокировки, и если да, то прервал скрипт.

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

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

Author: Cristian Ciupitu, 2012-05-11

9 answers

Консультативная блокировка сделана именно для этой цели.

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

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

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

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

 44
Author: Ja͢ck, 2013-12-30 23:10:27

Может быть, лучше не писать код, если вы можете его настроить:

Https://serverfault.com/questions/82857/prevent-duplicate-cron-jobs-running

 15
Author: Leonid Shagabutdinov, 2017-04-13 12:13:38

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

$filehandle = fopen("lock.txt", "c+");

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)) 
 12
Author: mgapatrick, 2013-09-04 15:45:25

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

Https://gist.github.com/coderofsalvation/1102e56d3d4dcbb1e36f

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

 1
Author: coderofsalvation, 2016-01-10 18:25:30

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

 0
Author: Ganesh Bora, 2014-10-21 08:24:55

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

<?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");
?>
 0
Author: Dragos, 2014-10-29 07:46:57
#!/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
Author: Ashwini Kumar Maurya, 2015-02-23 09:12:58

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

<?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/9539178

 0
Author: joseantgv, 2015-07-02 14:12:27

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

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

 0
Author: Growling Flea, 2017-10-22 20:21:00