2011-05-27 11 views
15

Necesito ejecutar un script de python (job.py) cada minuto. Este script no debe iniciarse si ya se está ejecutando. Su tiempo de ejecución puede ser de entre 10 segundos y varias horas.Ejecutar script de Python con cron solo si no se ejecuta

por lo que poner en mi crontab:

* * * * * root cd /home/lorenzo/cron && python -u job.py 1>> /var/log/job/log 2>> /var/log/job/err 

evitar comenzar la secuencia de comandos cuando ya se está ejecutando, uso flock().

Este es el guión (job.py):

import fcntl 
import time 
import sys 

def doIncrediblyImportantThings(): 
    for i in range (100): 
     sys.stdout.write ('[%s] %d.\n' % (time.strftime ('%c'), i)) 
     time.sleep (1) 

if __name__ == '__main__': 
    f = open ('lock', 'w') 
    try: fcntl.lockf (f, fcntl.LOCK_EX | fcntl.LOCK_NB) 
    except: 
     sys.stderr.write ('[%s] Script already running.\n' % time.strftime ('%c')) 
     sys.exit (-1) 
    doIncrediblyImportantThings() 

Este enfoque parece funcionar.

¿Hay algo que me falta? ¿Hay algún problema que pueda encontrar al usar este enfoque?

¿Existen formas más recomendadas o "adecuadas" para lograr este comportamiento?

Gracias por cualquier sugerencia.

+0

Muy buena pregunta. – erjoalgo

Respuesta

9

La única sugerencia que haría es hacer que su manejo de excepciones sea un poco más específico. No desea eliminar accidentalmente la importación fcntl un día y ocultar el NameError que resulta. Siempre trate de atrapar la excepción más específica que desee manejar. En este caso, le sugiero algo como:

import errno 

try: 
    fcntl.lock(...) 
except IOError, e: 
    if e.errno == errno.EAGAIN: 
     sys.stderr.write(...) 
     sys.exit(-1) 
    raise 

De esta manera, cualquier otra causa del bloqueo de ser inalcanzable muestra (probablemente en su correo electrónico ya que estás usando cron) y usted puede decidir si es algo para que lo vea un administrador, otro caso para que el programa lo maneje, o alguna otra cosa.

+0

Gracias. Buen punto. – Hyperboreus

2

Tiene problemas cuando la máquina se reinicia o se congela con la secuencia de comandos en ejecución (y por lo tanto un bloqueo activo). Una forma simple de contrarrestar esto es usar la marca de tiempo cron @reboot para ejecutar rm /path/to/lock.

+0

Muchas gracias. Tomaré esto en consideración. ¿Los bloqueos de archivos son persistentes durante el reinicio?Depende de esto en el sistema de archivos que estoy usando (en realidad ext4)? – Hyperboreus

+2

Los bloqueos de archivos no son persistentes durante los reinicios. Ni siquiera son persistentes en el reinicio de procesos, por lo que no es necesario liberar el bloqueo en el código: se libera cuando finaliza el proceso. –

+0

@ Jean-Paul Esto significa que no tengo que preocuparme de reiniciar y congelar como Mel dice? – Hyperboreus

0

Puede usar The Fat Controller, que es un daemon que reiniciará una secuencia de comandos x segundos después de que la instancia anterior finalizó, por lo que nunca podrá tener instancias superpuestas de la misma secuencia de comandos.

Incluso puede sintonizarlo para iniciar una instancia inmediatamente después si se cumple una determinada condición.

(Me temo que el sitio web es un poco básico, pero el proyecto es estable y se está ejecutando al fin en varios sitios web que conozco. Crearé un sitio bonito y bonito una vez que obtenga v0.0.3 el puerta!)

+0

Gracias por la entrada. Pero creo que usar tu código sería como dispararle a las aves con cañones, ya que trae mucha más funcionalidad (ejecución paralela, etc.) de lo que realmente necesito. – Hyperboreus

2

Me encontré con este problema exacto la semana pasada, y aunque encontré algunas buenas soluciones, decidí hacer un paquete de python muy simple y limpio y lo cargué en PyPI.

Instalar con: pip install quicklock

uso es extremadamente simple:

[[email protected] ~/live] python 
Python 2.7.6 (default, Sep 9 2014, 15:04:36) 
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.39)] on darwin 
Type "help", "copyright", "credits" or "license" for more information. 
>>> from quicklock import singleton 
>>> # Let's create a lock so that only one instance of a script will run 
... 
>>> singleton('hello world') 
>>> 
>>> # Let's try to do that again, this should fail 
... 
>>> singleton('hello world') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "/Users/nate/live/gallery/env/lib/python2.7/site-packages/quicklock/quicklock.py", line 47, in singleton 
    raise RuntimeError('Resource <{}> is currently locked by <Process {}: "{}">'.format(resource, other_process.pid, other_process.name())) 
RuntimeError: Resource <hello world> is currently locked by <Process 24801: "python"> 
>>> 
>>> # But if we quit this process, we release the lock automatically 
... 
>>> ^D 
[[email protected] ~/live] python 
Python 2.7.6 (default, Sep 9 2014, 15:04:36) 
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.39)] on darwin 
Type "help", "copyright", "credits" or "license" for more information. 
>>> from quicklock import singleton 
>>> singleton('hello world') 
>>> 
>>> # No exception was thrown, we own 'hello world'! 

Echale un vistazo: https://pypi.python.org/pypi/quicklock

Cuestiones relacionadas