Php 如果cron作业已在运行,如何防止其执行
我有一个php脚本,我在CentOS上每10分钟通过cron执行一次这个脚本 问题是,如果cron作业将花费10分钟以上,那么相同cron作业的另一个实例将启动 我试过一个技巧,那就是:Php 如果cron作业已在运行,如何防止其执行,php,cron,centos,Php,Cron,Centos,我有一个php脚本,我在CentOS上每10分钟通过cron执行一次这个脚本 问题是,如果cron作业将花费10分钟以上,那么相同cron作业的另一个实例将启动 我试过一个技巧,那就是: 创建一个带有php代码的锁文件(与pid文件相同) cron作业已启动 作业完成后,使用php代码删除了锁定文件 当任何新的cron作业开始执行脚本时,我检查是否锁定 文件存在,如果存在,则中止脚本 但有一个问题是,当脚本由于任何原因没有删除或删除锁文件时。 cron将永远不会再次启动 如果cron作业已经在运
如果cron作业已经在运行,是否有任何方法可以使用Linux命令或类似命令再次停止执行该作业?建议锁定正是出于此目的 您可以使用来完成建议锁定。只需将该函数应用于以前打开的锁文件,即可确定另一个脚本是否有锁
$f = fopen('lock', 'w') or die ('Cannot create lock file');
if (flock($f, LOCK_EX | LOCK_NB)) {
// yay
}
在本例中,我添加了LOCK\u NB
,以防止下一个脚本等待第一个脚本完成。因为您使用的是cron,所以总会有下一个脚本
如果当前脚本提前终止,操作系统将释放任何文件锁。如果可以配置代码,最好不要编写代码:
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作业,只需暂停它直到运行的作业完成,那么只需省略锁\u NB
:
if (flock($filehandle, LOCK_EX))
flock在PHP5.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");
?>
另一种选择:
<?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);
}
}
}
这是一个非常常见的问题,有一个非常简单的解决方案:cronjoblock
一个简单的8行shell脚本包装器使用flock应用锁定:
顺便说一句,cronjoblock
还逆转了cron的垃圾邮件行为:只有在内容出错时才输出内容。这对于cron的MAILTO变量很方便。除非给定进程的exitcode>0,否则stdout/stderr输出将被抑制(因此cron不会发送邮件)
我正在运行一个php cron作业脚本,该脚本专门处理使用现有API发送文本消息的问题。在我本地的盒子上,cron工作正常,但在我客户的盒子上,它发送了双重消息。虽然这对我来说没有意义,但我仔细检查了负责发送邮件的文件夹的权限,并将权限设置为root。一旦我将所有者设置为www-data(Ubuntu),它就开始正常运行
这可能不是您的问题,但如果是一个简单的cron脚本,我会仔细检查权限 谢谢杰克的回答。这是最好的解决办法。但我担心的是,如果cron由于任何原因(可能是电源故障或系统重新启动)停止,并且无法释放锁。cron将不会再次启动,因为它无法再次获取锁。我想我应该先试试这个:)不可能。无论进程何时停止,操作系统都会释放锁,h00ligan的链接文章需要一些丑陋的本地化攻击。这应该是公认的答案。它完全按照我的预期工作。感谢allot Jack。如果您的脚本因任何原因崩溃,cron将永远不会再次运行。(编辑)我的建议仅作为解决方案发布,以防flock()不起作用,我建议您首先尝试Jack的解决方案。我测试了使用,但出于某种原因,它并没有停止cron脚本的多个实例。因此,经过一点搜索之后,我找到了这个类,并对它进行了轻微的修改,以供我自己使用:使它成为一个服务:forever(){do_stuff();sleep_10_minutes()}
(伪代码)@Leonid Shagabutdinov的答案应该被接受为这个问题的正确解决方案。
<?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);
}
}
}