Django-Celery-Redis异步发邮件
- python == 3.7.6
- django == 2.1.8
- celery == 4.4.7
- redis == 3.5.3
- eventlet == 0.26.1
什么是celery
Celery 是一个功能完备、即插即用的任务队列,用起来非常省心。它最大的价值在于让开发者不必纠结于复杂的底层实现,轻轻松松就能把那些耗时的操作——比如发邮件、上传文件、图像处理——扔给后台异步执行,用户不用在那干等着,体验自然就上来了。

Celery 有几个很讨喜的特点:
- 简单:上手快,文档丰富,维护成本低。
- 高效:单个进程每分钟能处理数百万个任务,性能杠杠的。
- 灵活:几乎每个环节都支持自定义扩展,想怎么玩都行。
celery核心
1、Task
任务(Task)就是你要干的事。比如用户注册流程里,给用户发验证邮件就是一个典型任务——这种耗时操作交给 Celery 处理再合适不过。还有一种任务是定时任务,比如每天凌晨统计网站注册人数,Celery 也能周期性地搞定。
2、Broker
Broker 直译是“经纪人”,在 Celery 里它扮演着生产者和消费者之间的中间人,本质上就是一个消息队列。举个例子:Web 程序在生产业务时,遇到发信息这种耗时操作,就把任务丢给 Broker 暂存,然后 Worker 这个后台服务会实时盯着队列,一旦有新任务就取出来执行。Celery 本身不提供队列服务,通常用 Redis 或 RabbitMQ 来充当 Broker 的角色。
3、Worker
Worker 就是那个一直在后台默默干活的人,也叫任务的消费者。它实时监控队列,有新任务就立刻取出来执行,勤勤恳恳。
4、Beat
Beat 是定时任务调度器,它按照配置好的时间表,把任务定时发送给 Broker,等着 Worker 来消费。
5、Backend
Backend 用来保存任务的执行结果。每个任务都有返回值,比如发邮件到底成功了没有,这个结果就存在 Backend 里。当然,如果你不关心结果,也可以不配置。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WAJ9e74O-1606987087487)(django-celery-redis异步celery-01.png)]
Broker选择
Celery 需要一种机制来发送和接收消息,这种中间件就叫 message broker(消息中间人)。目前主流的选择有两个:
RabbitMQ
RabbitMQ 功能完备、稳定可靠,而且安装简单。生产环境里,它是最优的选择。具体配置可以参考官方文档:https://docs.celeryproject.org/en/latest/getting-started/brokers/rabbitmq.html#broker-rabbitmq
Redis
Redis 同样是一个功能完备的 broker 选项,不过它有个小毛病:遇到意外中断或电源故障,数据可能会丢失。如果要用 Redis 当 Broker,配置方法见:https://docs.celeryproject.org/en/latest/getting-started/brokers/redis.html#broker-redis
将redis发布订阅模式用做消息队列和rabbitmq的区别
可靠性
Redis 的发布订阅模式没有消息确认机制,如果发布者发了一条消息,却没有订阅者在监听,那这条消息就丢了。而 RabbitMQ 有消费确认机制,消息发出去后如果没有消费者消费,它会一直待在队列里,直到被消费为止,可靠性更高。
实时性
Redis 作为缓存服务器,数据全在内存里,所以实时性更高。但 RabbitMQ 的队列可以被多个消费者同时监控,每条消息只能被一个消费者消费,加上消费确认机制,它能根据消费者的处理能力自动调整负载。Redis 的发布订阅则是把消息依次发给所有订阅者,没有负载均衡的概念。
持久性
Redis 的持久化是针对整个缓存实例的,有 RDB 和 AOF 两种方式,主要用于数据备份和异常恢复,粒度比较粗。RabbitMQ 则支持队列和消息的单独持久化,粒度更细,更灵活。另外,RabbitMQ 自带后台监控平台,可以直观地看到所有队列的详细信息;Redis 没有这样的监控平台。
总结:
- Redis:轻量级、低延迟、高并发,但可靠性较低。
- RabbitMQ:重量级、高可靠、异步,实时性不保证。
在Django中使用celery
django项目名称:celery_test
在 django 项目 celery_test/celery_test/ 下创建 celery.py 文件,写入以下内容:
# celery.py文件
import os
from celery import Celery
# 把celery和django进行组合,识别和加载django的配置文件
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'celery_test.settings')
# 创建celery实例
app = Celery('celery_test')
# 指定celery消息队列的配置
app.config_from_object('celery_test.config', namespace='CELERY')
# 从所有的django-app中加载任务
app.autodiscover_tasks()
@app.task(bind=True)
def debug_task(self):
print('Request: {0!r}'.format(self.request))
在 celery_test/celery_test/ 下创建 config.py 文件,配置消息队列和结果存储:
# 消息中间人设置 broker_url = 'redis://127.0.0.1:6379/15' # 结果存储设置 result_backend = 'redis://127.0.0.1:6379/14'
在 celery_test/celery_test/__init__.py 中写入:
# 绝对引用,使我们的celery模块不会与原始的celery冲突
from __future__ import absolute_import, unicode_literals
# 加入绝对引入以后,导入当前模块下的内容方法: from xx import xx as xx
from .celery import app as celery_app
__all__ = ('celery_app',)
在 settings.py 中配置邮件发送:
# 配置邮件发送 EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' EMAIL_HOST = 'smtp.qq.com' # 如果为163邮箱,设置为smtp.163.com EMAIL_PORT = 25 # 或者 465/587是设置了 SSL 加密方式 # 发送邮件的邮箱 EMAIL_HOST_USER = '373576175@qq.com' # 在邮箱中设置的客户端授权密码 EMAIL_HOST_PASSWORD = 'ajtbjznedksabiic' # 第三方登陆使用的授权密码 EMAIL_USE_TLS = True # 这里必须是 True,否则发送不成功 # 收件人看到的发件人, 必须是一直且有效的 EMAIL_FROM = '海上明月<373576175@qq.com>' DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
在 django 的 app 中创建 tasks.py,写入异步任务:
# 绝对引用,使我们的celery模块不会与原始的celery冲突
from __future__ import absolute_import, unicode_literals
# 导入原始的celery模块中shared_task from xx import xx
from celery import shared_task
# 使用django内置函数发送邮件
from django.core.mail import send_mail
# 导入django的settings
from django.conf import settings
@shared_task
def send_mail_task(usernaem, email, token):
"""
使用django内置函数发送邮件
"""
subject = "海上明月"
message = ""
sender = settings.EMAIL_FROM
recipient = [email]
html_message = "{},欢迎您注册,请点击以下链接进行激活邮箱:点击这里进行激活
".format(username, token)
send_mail(subject, message, sender, recipient, html_message=html_message)
在 app 的 views.py 中调用任务:
from django.views import View
from django.http import JsonResponse
from .tasks import send_mail_task
from itsdangerous import TimedJSONWebSignatureSerializer
from django.conf import settings
# 发送邮件
token_serializer = TimedJSONWebSignatureSerializer(settings.SECRET_KEY, 600)
class SendMailView(View):
def post(self, request):
username = 'Edward'
email = '370686999@qq.com'
user_info = {'user_id': '1'}
token = token_serializer.dumps(user_info).decode()
send_mail_task.delay(username, email, token)
return JsonResponse({'msg': 'OK'})
邮件发送成功后,用户通过 GET 方式携带 token 激活:
from django.views import View
from django.http import JsonResponse
from itsdangerous import TimedJSONWebSignatureSerializer, SignatureExpired
from django.conf import settings
# 发送邮件
token_serializer = TimedJSONWebSignatureSerializer(settings.SECRET_KEY, 600)
class ActiveView(View):
"""
激活用户
"""
def get(self, request, token):
try:
user_info = token_serializer.loads(token)
return JsonResponse({'msg': '激活成功', 'code': 200})
except SignatureExpired:
return JsonResponse({'msg': '激活链接失效,请重新发送邮件进行激活', 'code': 404})
启动celery
打开 PyCharm 的 Terminal,进入项目根目录,使用 eventlet 启动 celery:
celery -A celery_test worker -l info -P eventlet
总结
以上就是 Django 中集成 Celery + Redis 实现异步发邮件的完整流程,从配置到代码,一步步走下来其实并不复杂。关键是把 Celery 的核心组件(Broker、Worker、Backend)理解清楚,再根据项目需求选择合适的中间件。希望这篇教程能帮你快速上手,少踩几个坑。
