Как предотвратить выполнение задания cron, если оно уже запущено
У меня есть один php-скрипт, и я выполняю этот скрипт через cron каждые 10 минут на CentOS.
Проблема в том, что если задание cron займет более 10 минут, то запустится другой экземпляр того же задания cron.
Я попробовал один трюк, а именно:
- Создал один файл блокировки с php-кодом (такой же, как pid-файлы) при запуске задания cron.
- Удалил файл блокировки с кодом php, когда работа была завершена.
- И когда начиналось какое-либо новое задание cron выполняя скрипт, я проверил , существует ли файл блокировки, и если да, то прервал скрипт.
Но может возникнуть одна проблема, когда файл блокировки не удаляется или не удаляется скриптом по какой-либо причине. Крон никогда не начнется снова.
Могу ли я каким-либо образом снова остановить выполнение задания cron, если оно уже запущено, с помощью команд Linux или подобных этому?
9 answers
Консультативная блокировка сделана именно для этой цели.
Вы можете выполнить консультативную блокировку с помощью flock()
. Просто примените функцию к ранее открытому файлу блокировки, чтобы определить, имеет ли другой скрипт блокировку.
$f = fopen('lock', 'w') or die ('Cannot create lock file');
if (flock($f, LOCK_EX | LOCK_NB)) {
// yay
}
В этом случае я добавляю LOCK_NB
, чтобы следующий сценарий не ждал завершения первого. Поскольку вы используете cron, всегда будет следующий сценарий.
Если текущий сценарий преждевременно завершится, любые блокировки файлов будут выпущено операционной системой.
Может быть, лучше не писать код, если вы можете его настроить:
Https://serverfault.com/questions/82857/prevent-duplicate-cron-jobs-running
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))
Это очень распространенная проблема с очень простым решением: cronjoblock
простая оболочка shellscript из 8 строк применяет блокировку с помощью flock:
Https://gist.github.com/coderofsalvation/1102e56d3d4dcbb1e36f
Кстати.
cronjoblock
также отменяет спам-рассылку cron: выводите что-то только в том случае, если что-то пойдет не так. Это удобно в отношении переменной cron MAILTO. Вывод stdout/stderr будет подавлен (поэтому cron не будет отправлять почту) , если не задано процесс имеет код выхода >0
Flock не будет работать в php 5.3.3, так как автоматическая разблокировка при закрытии дескриптора ресурсов файла была удалена. Разблокировка теперь всегда должна выполняться вручную.
Я использую это::
<?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");
?>
#!/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
Другая альтернатива:
<?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
Я запускал скрипт задания php cron, который специально занимался отправкой текстовых сообщений с использованием существующего API. На моем локальном ящике работа cron работала нормально, но на ящике моего клиента она отправляла двойные сообщения. Хотя для меня это не имеет смысла, я дважды проверил разрешения для папки, ответственной за отправку сообщений, и для разрешения было установлено значение root. Как только я установил владельца как www-данные (Ubuntu), он начал вести себя нормально.
Возможно, это не проблема для вас, но если это простой скрипт cron, я бы дважды проверил разрешения.