是否可以为celery的canvas原语使用自定义路由?

时间:2013-06-25 15:02:22

标签: celery django-celery

我有不同的Rabbit队列,每个队列都专门用于特殊的订单处理:

# tasks.py

@celery.task
def process_order_for_product_x(order_id):
    pass  # elided ...


@celery.task
def process_order_for_product_y(order_id):
    pass  # elided ...


# settings.py

CELERY_QUEUES = {
    "black_hole": {
        "binding_key": "black_hole",
        "queue_arguments": {"x-ha-policy": "all"}
    },
    "product_x": {
        "binding_key": "product_x",
        "queue_arguments": {"x-ha-policy": "all"}
    },
    "product_y": {
        "binding_key": "product_y",
        "queue_arguments": {"x-ha-policy": "all"}
    },

我们制定了一项政策,即通过设置CELERY_DEFAULT_QUEUE = 'black_hole'来强制执行显式路由,然后永远不会从black_hole消费。

这些任务中的每一个都可以使用celery的canvas原语,如下所示:

# tasks.py

@celery.task
def process_order_for_product_x(order_id):
    # These can run in parallel
    stage_1_group = group(do_something.si(order_id),
                          do_something_else.si(order_id))

    # These can run in parallel
    another_group = group(do_something_at_end.si(order_id),
                          do_something_else_at_end.si(order_id))

    # These run in a linear sequence
    process_task = chain(
        stage_1_group,
        do_something_dependent_on_stage_1.si(order_id),
        another_group)

    process_task.apply_async()

假设我希望celery.groupcelery.chordcelery.chord_unlock和其他画布任务的特定用途流过相应产品的队列,而不是陷入{{1有没有办法用自定义任务名称或自定义routing_key调用每个特定的画布任务?

由于我不会进入的原因,我宁愿不将所有black_hole任务发送到一个全能celery.*队列,这是我在此期间所做的。

2 个答案:

答案 0 :(得分:4)

此方法允许您将Celery画布任务路由到回调任务的队列。

here所述,可以为Celery指定基于类的自定义任务路由器。

让我们关注celery.chord_unlock任务。其签名定义为here

def unlock_chord(self, group_id, callback, ...):

第二个位置参数是和弦回调任务的签名。

Celery中的任务签名基本上都是dicts,因此我们有机会访问任务选项,包括任务队列名称。

以下是一个例子:

class CeleryRouter(object):
    def route_for_task(self, task, args=None, kwargs=None):
        if task == 'celery.chord_unlock':
            callback_signature = args[1]
            options = callback_signature.get('options')
            if options:
                queue = options.get('queue')
                if queue:
                    return {'queue': queue}

将其添加到Celery配置:

CELERY_ROUTES = (CeleryRouter(), 

答案 1 :(得分:0)

我目前正在我的项目中使用Celery。对于某些场景,我需要任务来链接不同的队列:

chain(get_staff.s(url), save_staff.s(dt, partner_id, url))()

这两个函数声明如下:

@task(queue='celery_gevent')
def get_staff(source_url):

@task # send to default queue
def save_staff(suggests, dt, partner, url):

btw, celery_gevent 由具有 gevent 池的工作人员处理,以发出http请求。

此示例,您可以如何隐式指定队列 。您也可以通过指定其他参数来显式将任务放在不同的队列中,如下所示:

In [1]: add.apply_async([4,5])
Out[1]: <AsyncResult: bda3dedd-c2c4-44db-be8e-6a97e718f8b0>

$ sudo rabbitmqctl list_queues
Listing queues ...
celery  1
...done.

In [2]: add.apply_async([4,5], queue='your_product')
Out[2]: <AsyncResult: 934f6161-298b-468b-9716-3da6fae58fa5>

$ sudo rabbitmqctl list_queues
Listing queues ...
celery  1
your_product    1
...done.

您可以在自定义队列中运行整个画布:

process_task.apply_async(queue='your_queue')

尝试在@task装饰器中指定queue_name。这应该有所帮助。

链接:

http://docs.celeryproject.org/en/latest/reference/celery.app.task.html

http://docs.celeryproject.org/en/latest/_modules/celery/app/task.html#Task.apply_async