Django & Celery - проблемы с маршрутизацией
Я использую Django и Celery и пытаюсь настроить маршрутизацию к нескольким очередям. Когда я указываю задачи routing_key
а также exchange
(либо в декораторе задачи, либо используя apply_async()
), задача не добавляется в брокер (это Kombu, подключающийся к моей базе данных MySQL).
Если я укажу имя очереди в декораторе задачи (что будет означать, что ключ маршрутизации игнорируется), задача будет работать нормально. Кажется, это проблема с настройкой маршрутизации / обмена.
Есть идеи, в чем может быть проблема?
Вот настройки:
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)
Поэтому, когда вы применяете эту задачу с 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
Еще одна хорошая практика - не жестко кодировать имена очередей в задачу и использовать 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(), )