如何在特定vhost上处理Celery任务?

时间:2017-11-01 06:16:52

标签: python django celery

我有一个Celery任务,如:

from celery.task import task
from django.conf import settings
from base.tasks import BaseTask

@task(name="throw_exception", base=BaseTask)
def print_value(*args, **kwargs):
    print('BROKER_URL:', settings.BROKER_URL)

我在我的virtualenv中运行一个Celery工作人员,如:

celery worker -A myproject -l info

工人表示:

Connected to amqp://guest:**@127.0.0.1:5672/myapp

当我从Django shell启动我的任务时:

>>> from django.conf import settings
>>> settings.BROKER_URL
'amqp://guest:**@127.0.0.1:5672/myapp'
>>> from myapp.tasks import print_value
>>> print_value.delay()

我从未在工作人员的日志中看到执行任务。

但是,如果我改变我的工作人员使用BROKER_URL并使用默认的" /" vhost,然后它会立即执行所有挂起的任务,这意味着即使设置了正确的BROKER_URL,我print_value.delay()的所有调用都会将它发送到错误的vhost。我做错了什么?

编辑:问题似乎是Celery没有一致的@task装饰器,并且通过使用错误的装饰器,您可以将任务与代理设置断开连接。基本上,我的所有任务都配置为使用默认代理,而不是我设置中定义的代理。旧文档说要使用from celery.task import task,但新文档......并没有真正指定,似乎暗示您应该使用app文件中定义的celery.py实例,比如@app.task。这样做的问题是我的所有任务都在单独的tasks.py文件中,他们无法访问app实例。如果我将任务复制到我的celery.py并使用@app.task装饰器,那么它会使用正确的vhost并按预期工作,但很明显,这不是实用的修复,因为我' d必须将许多函数复制到此文件中。我该如何妥善解决这个问题?

3 个答案:

答案 0 :(得分:2)

我现在使用 Django + (Celery + RabbitMQ) 有同样的问题。我的解决方案是,

CELERY_BROKER_URL=amqp://<user>:<password>@localhost:5672/<vhost>

这是来自 RabbitMQ.com 的详细确认 > 客户端文档 > RabbitMQ URI Specification


物有所值...

... Celery + RabbitMQ 有很多进展。我正在用 rabbitmqctl list_vhosts-- 看 RabbitMQ,但我没有看到我的虚拟主机。 WTF? 最后,我意识到我在本地开发服务器上配置 supervisord 太早了。从 CLI 启动 Celery 提供了一堆反馈,supervisord 将这些反馈放在了我不知道的地方,例如:

[2021-02-19 18:26:52,803: WARNING/MainProcess] (0, 0): (403) ACCESS_REFUSED - Login was refused using authentication mechanism AMQPLAIN. For details see the broker logfile.

AMQP 立刻让我想起了连接字符串。繁荣。这是你的vhost

答案 1 :(得分:1)

在深入了解Celery的代码之后,我能找到设置当前应用的唯一方法就是致电celery._state._set_current_app(app)。显然,这是一种内部方法,并非设计为以这种方式使用,但我无法找到任何其他方式将我的自定义应用程序实例明确设置为&#34; current&#34;应用程序。我原以为这应该是自动完成的,特别是因为我的代码是直接从教程中获取的,所以文档不完整或者这是一个bug。

无论如何,工作芹菜文件如下:

from __future__ import absolute_import, print_function
import os
import sys

from celery import Celery
from celery._state import _set_current_app
import django

app = Celery('myproject')

app.config_from_object('django.conf:settings', namespace='CELERY')
_set_current_app(app)

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings.settings')
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../myproject')))
django.setup()
from django.conf import settings
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)

这导致我所有的tasks.py文件中的所有@task修饰符都能正确访问我的自定义Celery实例。

答案 2 :(得分:0)

只是提供一个演示用djcelery和不同的vhost进行测试。

项目文件夹中的

__init__.py

from __future__ import absolute_import
# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
from .celery import app as celery_app

celery.py,将SchoolMS替换为您自己的项目标签:

from __future__ import absolute_import
import os
from celery import Celery, platforms
from django.conf import settings

# set the default Django settings module for the 'celery' program.  
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'SchoolMS.settings')

app = Celery('SchoolMS')
platforms.C_FORCE_ROOT = True

# Using a string here means the worker will not have to  
# pickle the object when using Windows.  
app.config_from_object('django.conf:settings')
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)

settings.py

BROKER_URL = 'amqp://schoolms:schoolms@localhost:5672/schoolms'
CELERY_TIMEZONE = 'Asia/Shanghai'
CELERYBEAT_SCHEDULER = 'djcelery.schedulers.DatabaseScheduler'
CELERYBEAT_SCHEDULE = {
}

user/tasks.py

from celery import task

from django.conf import settings


@task
def send_tel_verify(tel_verify_id):
    try:
        tel_verify = TelVerify.objects.get(id=tel_verify_id)
        try:
            send_sms(tel_verify.tel, 'xxxx')
            return ''success'
        except SmsError as e:
            return 'error'
    except ObjectDoesNotExist:
        return 'not found'

user/views.py

send_tel_verify.delay(tel_verify.id)