使用Celery,两个工作进程同时运行唯一任务

时间:2018-04-11 15:46:26

标签: concurrency redis celery

我正在开发一个项目,其目标是运行一个守护进程,该守护进程将任务发送到Celery队列,Redis用作代理。每个任务必须一次处理一次(不允许并发)。

为了执行此操作,我将以下代码段实现到我的守护程序中,该守护程序充当Redis的锁:

while True:    
    for foo in bar:
        if not self.redis_client.exists(foo.name):
           # Send the task to the Celery queue
           task = celery_app.send_task('buzz', context={'name': foo.name})
           redis_client.send(foo.name, task.id)
    time.sleep(10)

一旦任务完成或失败,任务本身就会释放锁。

由于某些我不理解的原因,该任务有时会由两个工作进程同时运行:

[2018-04-11 15:23:45,705: INFO/ForkPoolWorker-1] Task has been executed in 101.43s for foo
[2018-04-11 15:23:45,881: INFO/ForkPoolWorker-4] Task has been executed in  114.66s for foo

它不会经常发生,但我不希望它发生。有什么可以解释这种行为?是否与Redis写入键/值对的开销时间有关?

作为附加信息,我还在同一台服务器上运行了一个Flower实例。

1 个答案:

答案 0 :(得分:1)

这里有很多遗漏的细节,但我会尽力帮助: 由于你的要求 - 没有并发性 - 我猜你只有一个芹菜工人在运行。 运行此worker时,可以通过-c标志(或--concurrency)指定并发级别 - 确保将其设置为1,这样一次只能生成该worker的一个实例。 ref here

例如:celery -A proj worker --loglevel=INFO --concurrency=1 -n worker1@%h

您应该注意的另一件事是worker_prefetch_multiplier,默认情况下一次预取4条消息。您可能也想将其更改为1(我猜您没有描述您的完整方案)。 ref here

最后,关于你的redis锁,考虑使用SETNX(如果不存在则设置) - 更多信息 - here

祝你好运!