web-dev-qa-db-fra.com

Comment empêcher l'exécution de la tâche cron, si elle est déjà en cours d'exécution

J'ai un script php, et j'exécute ce script via cron toutes les 10 minutes sur CentOS.

Le problème est que si le travail cron dure plus de 10 minutes, une autre instance du même travail cron démarre.

J'ai essayé une astuce, c'est:

  1. Créé un fichier de verrouillage avec du code php (identique aux fichiers pid) au démarrage de la tâche cron.
  2. Suppression du fichier de verrouillage avec le code php lorsque le travail est terminé.
  3. Et quand un nouveau travail cron a commencé l'exécution du script, j'ai vérifié si le fichier de verrouillage existe et si oui, j'ai abandonné le script.

Mais il peut y avoir un problème lorsque le fichier de verrouillage n'est pas supprimé ou supprimé par le script pour une raison quelconque. Le cron ne recommencera plus jamais.

Existe-t-il un moyen d'arrêter à nouveau l'exécution d'un travail cron s'il est déjà en cours d'exécution, avec des commandes Linux ou similaires?

26
Sanjay

Le verrouillage consultatif est fait exactement dans ce but.

Vous pouvez effectuer un verrouillage consultatif avec flock() . Appliquez simplement la fonction à un fichier de verrouillage précédemment ouvert pour déterminer si un autre script possède un verrou.

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

Dans ce cas, j'ajoute LOCK_NB pour empêcher le prochain script d'attendre la fin du premier. Puisque vous utilisez cron, il y aura toujours un prochain script.

Si le script actuel se termine prématurément, tous les verrous de fichiers seront libérés par le système d'exploitation.

45
Ja͢ck

Il est peut-être préférable de ne pas écrire de code si vous pouvez le configurer:

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

15

flock() a très bien fonctionné pour moi - j'ai un travail cron avec des requêtes de base de données programmées toutes les 5 minutes, donc ne pas en avoir plusieurs en même temps est crucial. C'est ce que j'ai fait:

$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);

Dans le cas où vous ne voulez pas tuer le prochain travail cron planifié, mais simplement le suspendre jusqu'à ce que celui en cours soit terminé, puis omettez simplement le LOCK_NB:

if (flock($filehandle, LOCK_EX)) 
12
mgapatrick

Il s'agit d'un problème très courant avec une solution très simple: cronjoblock un simple wrapper shellscript de 8 lignes applique le verrouillage à l'aide de flock:

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

btw. cronjoblock inverse également le comportement de courrier indésirable de cron: ne produire quelque chose que si les choses tournent mal. C'est pratique en ce qui concerne la variable MAILTO de cron. La sortie stdout/stderr sera supprimée (donc cron n'enverra pas de mails) sauf si le processus donné a un code de sortie> 0

1
coderofsalvation
#!/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
Ashwini Kumar Maurya

J'exécutais un script de travail cron php qui traitait spécifiquement de l'envoi de messages texte à l'aide d'une API existante. Sur ma boîte locale, le travail cron fonctionnait bien, mais sur la boîte de mon client, il envoyait des messages doubles. Bien que cela n'ait pas de sens pour moi, j'ai vérifié les autorisations du dossier responsable de l'envoi des messages et l'autorisation a été définie sur root. Une fois que j'ai défini le propriétaire comme www-data (Ubuntu), il a commencé à se comporter normalement.

Cela pourrait être le problème pour vous, mais si c'est un simple script cron, je revérifierais les autorisations.

0
Growling Flea

J'utilise ceci ::

<?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
Dragos

flock ne fonctionnera pas dans php 5.3.3 car le déverrouillage automatique lorsque le handle de ressource du fichier est fermé a été supprimé. Le déverrouillage doit désormais toujours être effectué manuellement.

0
Ganesh Bora

Une autre alternative:

<?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);
        }
    }
}

Source: https://Gist.github.com/wcurtis/9539178

0
joseantgv