33

我有一个 php 脚本,我在 CentOS 上每 10 分钟通过 cron 执行这个脚本。

问题是如果 cron 作业需要超过 10 分钟,那么同一个 cron 作业的另一个实例将启动。

我尝试了一个技巧,那就是:

  1. cron 作业开始时使用 php 代码创建一个锁定文件(与 pid 文件相同)。
  2. 作业完成时使用 php 代码删除锁定文件。
  3. 当任何新的 cron 作业开始执行脚本时,我检查了锁定文件是否存在,如果存在,则中止脚本。

但是可能存在一个问题,即当锁定文件由于某种原因没有被脚本删除或删除时。cron 将永远不会再次启动。

如果 cron 作业已经在运行,我有什么办法可以再次停止执行它,使用 Linux 命令或类似的命令?

4

9 回答 9

54

咨询锁定正是为此目的而设计的。

您可以使用flock(). 只需将该函数应用于先前打开的锁定文件,以确定另一个脚本是否对其进行了锁定。

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

在这种情况下,我添加LOCK_NB以防止下一个脚本等到第一个脚本完成。由于您使用的是 cron,因此总会有下一个脚本。

如果当前脚本过早终止,操作系统将释放任何文件锁。

于 2012-05-11T13:12:32.277 回答
16

如果可以配置,也许最好不要编写代码:

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

于 2013-08-29T17:33:10.513 回答
15

flock()对我来说效果很好 - 我有一个每 5 分钟安排一次数据库请求的 cron 作业,因此不要同时运行多个请求是至关重要的。这就是我所做的:

$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)) 
于 2013-09-04T15:45:25.547 回答
1

这是一个非常常见的问题,解决方案非常简单:cronjoblock一个简单的 8 行 shellscript 包装器使用flock 应用锁定:

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

顺便提一句。cronjoblock还可以逆转 cron 的垃圾邮件行为:仅在出现问题时才输出。这对于 cron 的 MAILTO 变量很方便。除非给定进程的 exitcode > 0 ,否则stdout/stderr 输出将被抑制(因此 cron 不会发送邮件)

于 2016-01-09T06:47:51.553 回答
0

flock 在 php 5.3.3 中不起作用,因为文件资源句柄关闭时的自动解锁已被删除。现在解锁总是必须手动完成。

于 2014-10-21T08:24:55.657 回答
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");
?>
于 2014-10-29T07:46:57.577 回答
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
于 2015-02-23T09:12:58.820 回答
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/9539178

于 2015-07-02T14:12:27.447 回答
0

我正在运行一个 php cron 作业脚本,该脚本专门处理使用现有 API 发送文本消息。在我的本地机器上,cron 工作运行良好,但在我客户的机器上,它正在发送双重消息。虽然这对我没有意义,但我仔细检查了负责发送消息的文件夹的权限,并将权限设置为 root。一旦我将所有者设置为 www-data (Ubuntu),它就会开始正常运行。

这可能不是你的问题,但如果它是一个简单的 cron 脚本,我会仔细检查权限。

于 2017-10-22T20:21:00.660 回答