我正在用Celery替换一些自己开发的代码,但很难复制当前的行为。我希望的行为如下:
tasks
路由密钥将消息发布到user.created
交换。send_user_activate_email
和check_spam
。我尝试通过使用user_created
参数定义ignore_result=True
任务以及send_user_activate_email
和check_spam
的任务来实现此目的。
在我的配置中,我添加了以下路由和队列定义。当消息传递到user_created
队列时,它不会传递给其他两个队列。
理想情况下,邮件仅传递到send_user_activate_email
和check_spam
队列。使用vanilla RabbitMQ时,消息将发布到交换机,队列可以绑定到该交换机,但Celery似乎直接向队列发送消息。
我如何在Celery中实现上述行为?
CELERY_QUEUES = {
'user_created': {'binding_key':'user.created', 'exchange': 'tasks', 'exchange_type': 'topic'},
'send_user_activate_email': {'binding_key':'user.created', 'exchange': 'tasks', 'exchange_type': 'topic'},
'check_spam': {'binding_key':'user.created', 'exchange': 'tasks', 'exchange_type': 'topic'},
}
CELERY_ROUTES = {
'user_created': {
'queue': 'user_created',
'routing_key': 'user.created',
'exchange': 'tasks',
'exchange_type': 'topic',
},
'send_user_activate_email': {
'queue': 'user_created',
'routing_key': 'user.created',
'exchange': 'tasks',
'exchange_type': 'topic',
},
'check_spam': {
'queue': 'user_created',
'routing_key': 'user.created',
'exchange': 'tasks',
'exchange_type': 'topic',
},
}
答案 0 :(得分:0)
听起来你期望一条消息被两个队列触发/消耗,但这不是Celery的工作方式。 Exchange会将任务发布到符合条件的队列,但一旦被使用,其他队列将忽略该消息。每个要触发的任务都需要一条消息。
新的Celery用户经常会感到困惑,因为" Queue"有两种用途。在这个系统中; Queue()和文档引用的Kombu队列,以及AMQP队列,它们直接保存消息并由工作人员使用。当我们发布到队列时,我们会想到AMQP,这是不正确的。 (感谢下面的回答)。
回到你的问题,如果我理解正确,当使用user_created时,你希望它产生另外两个任务; send_user_activate_email和check_spam。此外,这些不应相互依赖;它们可以在不同的机器上并行运行,而不需要知道彼此的状态。
在这种情况下,您希望user_created为" apply_async"这两个新任务并返回。这可以直接完成,也可以使用Celery" Group"包含check_spam和send_user_activate_email来实现这一目标。该小组给出了一些不错的速记,并为你的任务提供了一些结构,所以我个人推动你这个方向。
#pseudocode
group(check_spam.s(... checkspam kwargs ...), send_user_activate_email.s(... active email kwargs ...)).apply_async()
此设置将创建四条消息;一个用于您要执行的每个任务,另外一个用于Group(),它本身将具有结果。
在您的情况下,我不确定Exchange或ignore_result是否必要,但我需要查看任务代码并更多地了解系统以做出判断。
http://docs.celeryproject.org/en/latest/userguide/canvas.html#groups http://celery.readthedocs.org/en/v2.2.6/userguide/routing.html#exchanges-queues-and-routing-keys Why do CELERY_ROUTES have both a "queue" and a "routing_key"?
(如果我离开了,我将删除/删除答案......)
答案 1 :(得分:0)
解决问题和解决问题的简便方法是使用Celery工作流程
但首先我要改变你的队列定义,为每个任务设置一个唯一的路由键,并将exchange_type设置为'direct'值。
根据{{3}},直接交换匹配精确路由密钥,因此我们将相同的交换设置为所有自定义任务和使用者队列,并映射routing_key(用于任务)和binding_key(用于队列)就像下一个片段一样:
CELERY_QUEUES = {
'user_created': {'binding_key':'user_created', 'exchange': 'tasks', 'exchange_type': 'direct'},
'send_user_activate_email': {'binding_key':'send_user_activate_email', 'exchange': 'tasks', 'exchange_type': 'direct'},
'check_spam': {'binding_key':'check_spam', 'exchange': 'tasks', 'exchange_type': 'direct'},
}
CELERY_ROUTES = {
'user_created': {
'queue': 'user_created',
'routing_key': 'user_created',
'exchange': 'tasks',
'exchange_type': 'direct',
},
'send_user_activate_email': {
'queue': 'send_user_activate_email',
'routing_key': 'send_user_activate_email',
'exchange': 'tasks',
'exchange_type': 'direct',
},
'check_spam': {
'queue': 'check_spam',
'routing_key': 'check_spam',
'exchange': 'tasks',
'exchange_type': 'direct',
},
}
完成此更改后,您需要对可用列表使用适当的工作流程(celery documentation)。阅读你的问题我认为你需要一个链,因为需要保留订单。
sequential_tasks = []
sequential_tasks.append(user_created.s(**user_created_kwargs))
sequential_tasks.append(send_user_activate_email.s(**send_user_activate_email_kwargs))
sequential_tasks.append(check_spam.s(**check_spam_kwargs))
#you can add more tasks to the chain
chain(*sequential_tasks)()
Celery将透明地处理与队列相关的工作。