使用主题交换

时间:2016-04-01 07:43:22

标签: python rabbitmq celery message-queue messaging

我正在用Celery替换一些自己开发的代码,但很难复制当前的行为。我希望的行为如下:

  • 创建新用户时,应使用tasks路由密钥将消息发布到user.created交换。
  • 此消息应触发两个Celery任务,即send_user_activate_emailcheck_spam

我尝试通过使用user_created参数定义ignore_result=True任务以及send_user_activate_emailcheck_spam的任务来实现此目的。

在我的配置中,我添加了以下路由和队列定义。当消息传递到user_created队列时,它不会传递给其他两个队列。

理想情况下,邮件仅传递到send_user_activate_emailcheck_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',
    },
}

2 个答案:

答案 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将透明地处理与队列相关的工作。