2012-05-11 17 views
17

Mam jeden skrypt php, a ja wykonuję ten skrypt przez crona co 10 minut na CentOS.Jak zapobiec uruchomieniu zadania cron, jeśli jest już uruchomiony

Problem polega na tym, że jeśli zadanie cron zajmie więcej niż 10 minut, rozpocznie się inne wystąpienie tego samego zadania cron.

Próbowałem jeden trick, czyli:

  1. utworzono jeden plik zamek z kodu php (tak samo jak pliki PID) podczas rozpoczął zadanie cron.
  2. Usunięto plik blokady z kodem php po zakończeniu zadania.
  3. Kiedy każde nowe zadanie cron rozpoczęło wykonywanie skryptu, sprawdziłem, czy istnieje plik blokady , a jeśli tak, przerwałam skrypt.

Ale może istnieć jeden problem polegający na tym, że gdy plik blokady nie zostanie usunięty lub usunięty przez skrypt z jakiegokolwiek powodu. Cron nigdy się nie uruchomi.

Czy istnieje sposób, aby ponownie zatrzymać wykonywanie zadania cron, jeśli jest już uruchomiony, z poleceniami systemu Linux lub podobnymi do tego?

+0

Ustaw jako usługę, która wykonuje: 'forever() {do_stuff(); sleep_10_minutes(); } '(pseudokod) – Yetti99

+0

@Leonid Shagabutdinov odpowiedź powinna być uznana za prawidłowe rozwiązanie tego problemu. – Dom

+0

** (Edytuj) Moja sugestia została zamieszczona jako rozwiązanie tylko w przypadku, gdy flock() nie działał, polecam najpierw wypróbować rozwiązanie Jacka. ** Testowałem używając ['flock()'] (http : //php.net/manual/en/function.flock.php), ale z jakiegoś powodu nie zatrzymało wielu wystąpień skryptu cron. Po drobnych poszukiwaniach znalazłem tę klasę i nieco ją zmodyfikowałem na własny użytek: http://abhinavsingh.com/blog/2009/12/how-to-use-locks-in-php-cron-jobs-to- avoid-cron-overlaps/ – h00ligan

Odpowiedz

39

Blokada doradcza jest wykonana właśnie w tym celu.

Możesz wykonać blokadę doradczą za pomocą flock(). Po prostu zastosuj funkcję do poprzednio otwartego pliku blokady, aby ustalić, czy inny skrypt ma blokadę.

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

W tym przypadku Dodaję LOCK_NB aby zapobiec kolejnej skrypt z oczekiwaniem na pierwszy zakończył. Ponieważ używasz crona, zawsze będzie następny skrypt.

Jeśli bieżący skrypt zostanie przedwcześnie zakończony, wszelkie blokady plików zostaną zwolnione przez system operacyjny.

+1

Dziękuję Jack za miłą odpowiedź. To najlepsze rozwiązanie. Ale martwię się tym, jeśli cron zatrzyma się z jakiegokolwiek powodu (może to być awaria zasilania lub restart systemu) i nie będzie w stanie zwolnić blokady. Następnie cron nie uruchomi się ponownie, ponieważ nie może ponownie uzyskać blokady. Myślę, że powinienem spróbować tego najpierw :) – Sanjay

+8

Niemożliwe. System operacyjny zwolni blokadę bez względu na to, kiedy proces się zatrzyma, dla którego linkowany artykuł h00ligan potrzebuje jakiegoś brzydkiego zlokalizowanego hacka. –

+1

To powinna być zaakceptowana odpowiedź. Działa dokładnie zgodnie z przeznaczeniem. Dzięki za wszystko Jack. – Latheesan

11

flock() było dla mnie - mam pracę cron z żądań baz danych zaplanowanych co 5 minut, więc nie mając kilka biegnących jednocześnie jest kluczem. To, co zrobiłem:

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

W przypadku, gdy nie chcesz zabić następnego zaplanowanego zadania cron, ale po prostu wstrzymać się aż do jednego z systemem zakończeniu, a następnie po prostu pominąć LOCK_NB:

if (flock($filehandle, LOCK_EX)) 
0

flock nie zadziała w php 5.3.3 jako Automatyczne odblokowanie po zamknięciu uchwytu zasobu pliku zostało usunięte. Odblokowanie teraz zawsze musi być wykonane ręcznie.

0

Używam tego ::

<?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"); 
?> 
+2

jeśli twój skrypt ulegnie awarii z jakiegokolwiek powodu, cron nigdy nie będzie działał ponownie. – Pooya

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 
0

Inną alternatywą:

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

Źródło: https://gist.github.com/wcurtis/9539178

1

jest to bardzo powszechny problem z bardzo prostego rozwiązania: cronjoblock się prosta 8-liniowa owijka powłoki chroni za pomocą stada:

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

btw. cronjoblock powoduje także odwrócenie spamerskiego adresu e-mail crona: generuje tylko coś, jeśli coś pójdzie nie tak. Jest to przydatne w odniesieniu do zmiennej MAILTO crona. Wyjście stdout/stderr będą tłumione (tak cron nie będzie wysyłać e-maile) chyba dany proces ma kodu wyjścia> 0

0

byłem uruchomienie skryptu pracy php cron, który zajmował specjalnie z wysyłaniem wiadomości tekstowych przy użyciu istniejące API. W moim lokalnym pudełku praca crona działała dobrze, ale na pudełku mojego klienta wysyłała podwójne wiadomości. Chociaż nie ma to dla mnie sensu, dwukrotnie sprawdziłem uprawnienia do folderu odpowiedzialnego za wysyłanie wiadomości, a uprawnienia zostały ustawione na root. Kiedy ustawiłem właściciela na www-dane (Ubuntu), zaczął działać normalnie.

To może być problem dla ciebie, ale jeśli jest to prosty skrypt crona, dwukrotnie sprawdziłbym uprawnienia.

Powiązane problemy