我有两个运行在两个不同服务器上的Django Celery代码实例,用于冗余访问另一个服务器上的公共数据库。我注意到当用户提交作业时,芹菜在两个服务器上同时启动了同一任务。这将创建竞争条件,并两次更新数据库。如何通过在另一台服务器中启动另一项类似任务的同时将任务保留在一台服务器中来防止这种情况发生?
答案 0 :(得分:0)
您需要创建一个锁以防止两个任务同时执行,celery文档http://ask.github.io/celery/cookbook/tasks.html中有一个页面,其中包含如何执行此操作的示例。小心,您的实现不会陷入某种死锁中,并且您会在锁上设置一个超时,以便在工作者崩溃时不会无限期地持有该锁。
# Example from the link above
from celery.task import Task
from django.core.cache import cache
from django.utils.hashcompat import md5_constructor as md5
from djangofeeds.models import Feed
LOCK_EXPIRE = 60 * 5 # Lock expires in 5 minutes
class FeedImporter(Task):
name = "feed.import"
def run(self, feed_url, **kwargs):
logger = self.get_logger(**kwargs)
# The cache key consists of the task name and the MD5 digest
# of the feed URL.
feed_url_digest = md5(feed_url).hexdigest()
lock_id = "%s-lock-%s" % (self.name, feed_url_hexdigest)
# cache.add fails if if the key already exists
acquire_lock = lambda: cache.add(lock_id, "true", LOCK_EXPIRE)
# memcache delete is very slow, but we have to use it to take
# advantage of using add() for atomic locking
release_lock = lambda: cache.delete(lock_id)
logger.debug("Importing feed: %s" % feed_url)
if acquire_lock():
try:
feed = Feed.objects.import_feed(feed_url)
finally:
release_lock()
return feed.url
logger.debug(
"Feed %s is already being imported by another worker" % (
feed_url))
return
此模式需要用于获取锁的缓存服务器。任务启动时,它将基于密钥(例如“ my_task”)在缓存服务器中获取一个锁,然后当任务完成时,它将释放该锁。已启动的任何其他任务可能应该具有一个while
循环,该循环等待直到可以获取锁为止。 Redis锁是原子的,这意味着获取锁的操作不会同时发生,并且只有一个任务将能够获取锁。