Django & Celery-проблемы маршрутизации

Я использую Django и сельдерей, и я пытаюсь настроить маршрутизацию на несколько очередей. Когда я указываю задачу routing_key и exchange (либо в декораторе задач, либо с помощью apply_async()), задача не добавляется к брокеру (который является Kombu, подключающимся к моей базе данных MySQL).

если я укажу имя очереди в декораторе задач (что будет означать, что ключ маршрутизации игнорируется), задача работает нормально. Похоже, это проблема с настройкой маршрутизации/exchange.

любая идея, что проблема может быть?

вот настройки:

settings.py

INSTALLED_APPS = (
    ...
    'kombu.transport.django',
    'djcelery',
)
BROKER_BACKEND = 'django'
CELERY_DEFAULT_QUEUE = 'default'
CELERY_DEFAULT_EXCHANGE = "tasks"
CELERY_DEFAULT_EXCHANGE_TYPE = "topic"
CELERY_DEFAULT_ROUTING_KEY = "task.default"
CELERY_QUEUES = {
    'default': {
        'binding_key':'task.#',
    },
    'i_tasks': {
        'binding_key':'important_task.#',
    },
}

tasks.py

from celery.task import task

@task(routing_key='important_task.update')
def my_important_task():
    try:
        ...
    except Exception as exc:
        my_important_task.retry(exc=exc)

инициировать задач:

from tasks import my_important_task
my_important_task.delay()

1 ответов


вы используете Django ORM в качестве брокера, что означает, что объявления хранятся только в памяти (см., бесспорно, трудно найти, таблицу сравнения транспорта на http://readthedocs.org/docs/kombu/en/latest/introduction.html#transport-comparison)

Итак, когда вы применяете эту задачу с routing_key important_task.update он не сможет чтобы направить его, потому что он еще не объявил очередь.

он будет работать, если вы делаете это:

@task(queue="i_tasks", routing_key="important_tasks.update")
def important_task():
    print("IMPORTANT")

но для вас было бы намного проще использовать функцию автоматической маршрутизации, поскольку здесь нет ничего, что показывает, что вам нужно использовать обмен " темой, использовать автоматическую маршрутизацию просто удалить параметры:

  • CELERY_DEFAULT_QUEUE,
  • CELERY_DEFAULT_EXCHANGE,
  • CELERY_DEFAULT_EXCHANGE_TYPE
  • CELERY_DEFAULT_ROUTING_KEY
  • CELERY_QUEUES

и объявить свою задачу, как это:

@task(queue="important")
def important_task():
    return "IMPORTANT"

а затем, чтобы запустить работника, потребляющего из этой очереди:

$ python manage.py celeryd -l info -Q important

или потреблять из обоих по умолчанию (celery очереди) и important очередь:

$ python manage.py celeryd -l info -Q celery,important

другой хорошей практикой является не hardcode имена очередей в задача и использование CELERY_ROUTES вместо:

@task
def important_task():
    return "DEFAULT"

потом в настройках:

CELERY_ROUTES = {"myapp.tasks.important_task": {"queue": "important"}}

если вы все еще настаиваете на использовании обмена темами, вы можете добавить это маршрутизатор для автоматического объявления всех очередей в первый раз задание отправляется:

class PredeclareRouter(object):
    setup = False

    def route_for_task(self, *args, **kwargs):
        if self.setup:
            return
        self.setup = True
        from celery import current_app, VERSION as celery_version
        # will not connect anywhere when using the Django transport
        # because declarations happen in memory.
        with current_app.broker_connection() as conn:
            queues = current_app.amqp.queues
            channel = conn.default_channel
            if celery_version >= (2, 6):
                for queue in queues.itervalues():
                    queue(channel).declare()
            else:
                from kombu.common import entry_to_queue
                for name, opts in queues.iteritems():
                    entry_to_queue(name, **opts)(channel).declare()
CELERY_ROUTES = (PredeclareRouter(), )