使用System V init脚本时,无法通过Celery worker在Django中发送电子邮件

时间:2015-04-27 14:48:37

标签: python django email celery

我有一个Django 1.6.2应用程序,我使用Celery 3.1.7和RabbitMQ 2.8.4来处理异步任务。我的问题是,如果电子邮件是通过Celery工作队列发送的,我无法使用Django的send_mail函数发送电子邮件。当我执行这个send_email任务时......

# apps.photos.tasks.py
from __future__ import absolute_import
from django.core.mail import send_mail
from conf.celeryapp import app

@app.task
def send_email():
    send_mail('Test Email',
        'This is a test email.',
        'me@example.com',
        ['me2@example.com'],
        fail_silently=False)

通过我的Python解释器中的这个命令...

>>> from apps.photos.tasks import send_email
>>> result = send_email.delay()

我收到以下错误:

[2015-04-24 15:13:48,371: ERROR/MainProcess] Task apps.photos.tasks.send_email[db303245-f36c-4ae2-9d72-86d2bbbbd358] raised unexpected: ImproperlyConfigured('Requested setting EMAIL_BACKEND, but settings are not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE or call settings.configure() before accessing settings.',)
Traceback (most recent call last):
  File "/home/myproj/venv/myproj/local/lib/python2.7/site-packages/celery/app/trace.py", line 240, in trace_task
    R = retval = fun(*args, **kwargs)
  File "/home/myproj/venv/myproj/local/lib/python2.7/site-packages/celery/app/trace.py", line 438, in __protected_call__
    return self.run(*args, **kwargs)
  File "apps/photos/tasks.py", line 34, in send_email
    fail_silently=False)
  File "/home/myproj/venv/myproj/local/lib/python2.7/site-packages/django/core/mail/__init__.py", line 48, in send_mail
    fail_silently=fail_silently)
  File "/home/myproj/venv/myproj/local/lib/python2.7/site-packages/django/core/mail/__init__.py", line 29, in get_connection
    klass = import_by_path(backend or settings.EMAIL_BACKEND)
  File "/home/myproj/venv/myproj/local/lib/python2.7/site-packages/django/conf/__init__.py", line 54, in __getattr__
    self._setup(name)
  File "/home/myproj/venv/myproj/local/lib/python2.7/site-packages/django/conf/__init__.py", line 47, in _setup
    % (desc, ENVIRONMENT_VARIABLE))

环境变量DJANGO_SETTINGS_MODULE在我的虚拟环境中设置。

只有当我通过这个celery守护程序初始化脚本启动我的worker时才会发生这种情况:

# /etc/default/celeryd
CELERYD_NODES="worker1 worker2 worker3"
CELERY_BIN="/home/myproj/venv/myproj/bin/celery"
CELERY_APP="conf.celeryapp:app"
CELERYD_CHDIR="/www/myproj"
CELERYD_OPTS="--time-limit=300 --concurrency=8 --config=celeryconfig -Q:1 default -Q:2 photos -Q:3 mail"
CELERYD_USER="celery"
CELERYD_GROUP="celery"

(我的Celery init脚本/etc/init.d/celeryd是Celery文档指出的标准脚本。)

如果我从命令行启动worker和队列,则会发送电子邮件而不会出现任何错误:

celery worker -A conf.celeryapp:app -n worker3 -Q mail -l info 

这是我的celeryconfig文件:

# /www/myproj/conf/celeryconfig.py
from kombu import Queue, Exchange

BROKER_URL = 'amqp://'
CELERY_RESULT_BACKEND = 'amqp://'
CELERY_DEFAULT_QUEUE = 'default'
CELERY_QUEUES = (
    Queue('default', Exchange('default'), routing_key='default'),
    Queue('photos', Exchange('photos'), routing_key='photos'),
    Queue('mail', Exchange('mail'), routing_key='mail'),
)
CELERY_ROUTES = (
    {'apps.photos.tasks.hello': {'queue': 'default', 'routing_key': 'default'}},
    {'apps.photos.tasks.add': {'queue': 'photos', 'routing_key': 'photos'}},
    {'apps.photos.tasks.send_email': {'queue': 'mail', 'routing_key': 'mail'}},
)

此文件初始化Celery应用程序:

# /www/myproj/conf/celeryapp.py
from celery import Celery

#import os
#os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'conf.settings.prod')

app = Celery('celeryapp')
app.config_from_object('conf.celeryconfig')
app.autodiscover_tasks(['apps.photos'])

这些设置将我的Django邮件配置为使用第三方电子邮件服务:

# /www/myproj/conf/settings/base.py
...
EMAIL_HOST = 'smtp.sendgrid.net'
EMAIL_HOST_USER = get_env_variable('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = get_env_variable('EMAIL_HOST_PASSWORD')
EMAIL_PORT = 587
EMAIL_USE_TLS = True
SITE_EMAIL = 'admin@example.com'
RECIPIENTS = ['me@example.com', ]
FAIL_SILENTLY = False
...

我试着取消注释" os.environ.setdefault" celeryapp.py中的命令认为Celery需要明确指向设置文件,但是当我这样做并尝试通过我的celeryd脚本使用命令" sudo service celeryd start"启动celery时,工作人员赢了' t启动,但Celery没有显示任何错误,即使我将loglevel设置为" debug"。

我还尝试在我的设置文件中明确设置EMAIL_BACKEND变量以及send_email函数本身......

settings.EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'

但是没有解决错误。

此外,我将以下内容添加到我的/ etc / default / celeryd文件中无效:

# /etc/default/celeryd
...
export DJANGO_SETTINGS_MODULE="conf.settings.prod"
...

任何人都可以看到我做错了吗?

谢谢!

1 个答案:

答案 0 :(得分:0)

这是因为你的工作人员根本不是Django应用程序,只有你的调度员才是。用于SMTP的Django配置实际上是用于直接从Django视图或Django信号处理程序的上下文发送电子邮件。 实际上,您需要做的是使用工作进程中的smtplib发送电子邮件,不要尝试读取django配置文件。相反,工作进程应该从类似JSON文件

的内容中读取SMTP配置参数