Django & Celery ― Routing problems

匿名 (未验证) 提交于 2019-12-03 00:44:02

问题:

I'm using Django and Celery and I'm trying to setup routing to multiple queues. When I specify a task's routing_key and exchange (either in the task decorator or using apply_async()), the task isn't added to the broker (which is Kombu connecting to my MySQL database).

If I specify the queue name in the task decorator (which will mean the routing key is ignored), the task works fine. It appears to be a problem with the routing/exchange setup.

Any idea what the problem could be?

Here's the setup:

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) 

Initiate task:

from tasks import my_important_task my_important_task.delay() 

回答1:

You are using the Django ORM as a broker, which means declarations are only stored in memory (see the, inarguably hard to find, transport comparison table at http://readthedocs.org/docs/kombu/en/latest/introduction.html#transport-comparison)

So when you apply this task with routing_key important_task.update it will not be able to route it, because it hasn't declared the queue yet.

It will work if you do this:

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

But it would be much simpler for you to use the automatic routing feature, since there's nothing here that shows you need to use a 'topic' exchange, to use automatic routing simply remove the settings:

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

And declare your task like this:

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

and then to start a worker consuming from that queue:

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

or to consume from both the default (celery) queue and the important queue:

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

Another good practice is to not hardcode the queue names into the task and use CELERY_ROUTES instead:

@task def important_task():     return "DEFAULT" 

then in your settings:

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

If you still insist on using topic exchanges then you could add this router to automatically declare all queues the first time a task is sent:

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(), ) 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!