Php 如果cron作业已在运行,如何防止其执行

Php 如果cron作业已在运行,如何防止其执行,php,cron,centos,Php,Cron,Centos,我有一个php脚本,我在CentOS上每10分钟通过cron执行一次这个脚本 问题是,如果cron作业将花费10分钟以上,那么相同cron作业的另一个实例将启动 我试过一个技巧,那就是: 创建一个带有php代码的锁文件(与pid文件相同) cron作业已启动 作业完成后,使用php代码删除了锁定文件 当任何新的cron作业开始执行脚本时,我检查是否锁定 文件存在,如果存在,则中止脚本 但有一个问题是,当脚本由于任何原因没有删除或删除锁文件时。 cron将永远不会再次启动 如果cron作业已经在运

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

问题是,如果cron作业将花费10分钟以上,那么相同cron作业的另一个实例将启动

我试过一个技巧,那就是:

  • 创建一个带有php代码的锁文件(与pid文件相同) cron作业已启动
  • 作业完成后,使用php代码删除了锁定文件
  • 当任何新的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);
            }
        }
    }