【发布时间】:2013-11-24 06:07:23
【问题描述】:
我有一个系统,其中一个 cron 作业用于每分钟驱动一个 manage.py 命令。
问题是,这项工作有时可能需要一分钟以上的时间,同时运行两个命令实例是不安全的。
有没有一种好方法可以让命令检测其自身的另一个实例是否已经在运行并提前退出?有没有更好的方法来达到同样的目的?
【问题讨论】:
我有一个系统,其中一个 cron 作业用于每分钟驱动一个 manage.py 命令。
问题是,这项工作有时可能需要一分钟以上的时间,同时运行两个命令实例是不安全的。
有没有一种好方法可以让命令检测其自身的另一个实例是否已经在运行并提前退出?有没有更好的方法来达到同样的目的?
【问题讨论】:
你也可以使用例如django-cronjobs(免责声明:我自己没用过)注册工作。来自文档:
# myapp/cron.py
import cronjobs
@cronjobs.register
def periodic_task():
pass
然后使用:
$ ./manage.py cron periodic_task
更重要的是:django-cronjob 甚至默认情况下会确保同时运行作业的一个副本。
【讨论】:
您可以做的是让命令在命令开始时创建一个文件,然后再执行它的任务(包含作业的 pid),然后在命令结束时清理该文件。
当命令运行时,它应该首先检查该 pidfile 是否存在。如果是这样,它不应该执行它的工作。
所以:
它并不完美(例如,如果命令没有正确完成,pidfile 不会被删除并且命令永远不会再次运行),但它可能足以满足您的情况。
【讨论】:
您可以使用 cronjob 库来处理锁定作业以防止多次执行 - Preventing multiple executions
您也可以使用celerybeat 而不是cron 来控制您的工作。 Celerybeat 会带来更多开销,但如果您已经在应用程序中使用了 celery,那么这应该不会太难。这列出了celerybeatWhat are the advantages of celerybeat over cron?的一些优点
您必须在某处保持状态以指示作业已在运行。 pid 技术很好,但另一种方法是使用Semaphore,方法是在缓存级别(Memcache/Redis)或直接在数据库中实现它。当可能没有一致的文件系统可用于管理 pid 文件时,这尤其有用。例如。您正在 Heroku 上运行您的应用程序。
另外,如果可以的话,尽量让你的 cron 作业具有幂等性,也就是说,即使作业并行运行多次,也不会产生副作用。
【讨论】:
我一直在使用lockfile,它工作得很好。
基本用法:
from lockfile import FileLock, AlreadyLocked, LockTimeout
lock = FileLock(lock_name)
try:
lock.acquire(LOCK_WAIT_TIMEOUT)
except AlreadyLocked:
logging.debug("lock already in place. quitting.")
return
except LockTimeout:
logging.debug("waiting for the lock timed out. quitting.")
return
logging.debug("acquired.")
# do stuff...
logging.debug("releasing lock...")
lock.release()
【讨论】: