在网站开发过程中,我们经常会给用户发送email进行注册激活等操作,然而发送邮件时需要时间的,在这个时间内后台程序是处于阻塞状态的,用户只能在邮件发出后才能得到响应,这严重影响用户体验。因此邮件的异步发送就变得尤为重要,celery是由python编写的简单、灵活且可靠的,处理大量消息的分布式系统,他是一个专注于实时处理的任务队列,但同时也支持任务调度。
django已经为我们内置了发送邮件的方法,只需要做相应的配置即可。
发邮件时我们需要先把邮件发送给smtp服务器,然后帮我们发送到目的邮箱,因此需要一个smtp服务器,常用的qq邮箱,163邮箱都有免费的smtp服务器,只需要开启即可,网上有很多教程我就不写了。我使用的是163邮箱的smtp服务器,开启后是这样的。
授权码一定要记住,发邮件时要用到。然后在django.settings中配置
# 发送邮件配置
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' #固定写法
EMAIL_HOST = 'smtp.163.com' #SMTP地址
EMAIL_PORT = 25 #SMTP端口
EMAIL_HOST_USER = '××××@163.com' #发送邮件的邮箱
EMAIL_HOST_PASSWORD = '××××' # 授权码
EMAIL_SUBJECT_PREFIX = '天天生鲜' #为邮件Subject-line前缀,默认是'[django]'
EMAIL_FROM = '天天生鲜<××××@163.com>' # 收件人看到的发件人,尖括号里的邮箱地址必须和EMAIL_HOST_USER一样
使用celery执行异步任务主要分为3个部分,首先要有一个任务发出者,还要有一个任务队列,用来储存任务,也就是中间人(broken),celery推荐使用RabbitMQ,我使用的是redis,最后还要有一个任务处理者(worker)来监听任务队列并处理任务。
网站给用户发送激活邮件时一般会发送一串携带可以作为用户唯一标识的信息作为链接地址让用户点击激活,这个唯一标识用户信息的链接如果直接暴露出来是非常不安全的,所以需要加密。我们可以使用itsdangerous对关键信息进行签名,安装pip install itsdangerous
。
导入
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
查看源码可以看到Serializer()需要两个参数:
secret_key:密钥,可以填任意字符串
expires_in:过期时间,默认3600秒,也就是1小时
对激活的url进行签名:
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer # 导入签名模块
from django.conf import settings
serializer = Serializer(secret_key=settings.SECRET_KEY, expires_in=3600) # 实例化一个签名对象
info = {"username":username} # 需要签名加密的信息,username从上文获取
token = serializer.dumps(info) # 进行签名加密
token = token.decode() # 签名之后的数据是一个字节流数据,需要转化成字符串
现在完成了url关键信息的加密,还需要使用celery异步发送邮件给用户进行激活。
首先,在项目下创建一个Celery_tasks目录,在目录里创建一个名为tasks.py的文件,作为处理异步任务的文件。
发送邮件时需要三个参数:用户的邮件地址,用户的用户名,签名加密的token。
加密传参完整代码
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from django.conf import settings
from Celery_tasks.tasks import register_active_email_task # 导入处理发送邮件的函数
def register(req):
if req.method == "GET":
pass
if req.method == "POST":
if req.POST.get("username"):
user = User()
user.username = username
user.password = password
user.email = email
user.save()
serializer = Serializer(secret_key=settings.SECRET_KEY, expires_in=3600)
info = {"username":username}
token = serializer.dumps(info)
token = token.decode()
register_active_email_task.delay(email,username,token) # register_active_email_task是处理发送邮件的函数,给该函数传递了email,username,token三个参数
error["status"] = "success"
v = json.dumps(error)
return HttpResponse(v)
参数传过去之后开始编写异步任务
tasks.py:
from django.core.mail import send_mail # django内置发送邮件的方法
from django.conf import settings
from celery import Celery
import os
import django
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'frenshshop.settings') # 项目环境初始化
django.setup() # django初始化
# 创建一个celery对象,连接储存任务的数据库,如果需要有返回值需要backend参数,同样是连接一个储存任务结果的数据库
app = Celery("Celery_tasks.tasks",broker="redis://127.0.0.1:6379/1")
# 创建异步任务
@app.task
def register_active_email_task(email,username,token): # 接受三个参数
subject = "天天生鲜激活账户" # 邮件的标题
message = "" # 邮件的内容,该内容不会显示html格式的文本,如需要html文本要使用html_message
from_email = settings.EMAIL_FROM # 发件人
recipient_list = [email] # 邮件接收人,为一个列表,可添加多个接收人
html_message = "<h1>{},欢迎注册天天生鲜</h1></br><p>请点击下面的链接激活账户:</p></br><a href='http://127.0.0.1:8000/register/active/{}'>http://127.0.0.1:8000/register/active/{} </a>".format(username,token,token) # html文本内容
send_mail(subject,message,from_email,recipient_list,html_message=html_message) # 发送邮件
用户点击链接之后还需要一个函数来处理这个链接,将用户的账户进行激活并通知用户。
配置路由
处理激活链接的函数
def active(req,token): # 将链接中签名加密的信息作为参数传入
serializer = Serializer(secret_key=settings.SECRET_KEY)
info = serializer.loads(token) # 对签名信息进行解密
username = info["username"] 获取唯一标识用户信息的用户名
User.objects.filter(username=username).update(is_active = 1) # 将该账户的状态变为激活状态
return HttpResponse("激活成功") # 通知用户账户激活,实际项目中应该返回一个页面
一些配置就绪,就可以启动项目了
首先启动redis
然后启动项目,正常启动就可以。
如果是windows系统,且安装的是celery4.0以上的版本,需要安装eventletpip install eventlet
,不然会报错not enough values to unpack (expected 3, got 0)
在项目目录下启动celery
celery -A Celery_tasks.tasks worker -l info -P eventlet
现在就可以执行发送邮件的异步任务了。
点击注册之后登录邮箱
就会收到一封邮件
内容如下
点击链接进行激活
看数据库可以发现该账户的激活状态已经从0变成了1,即激活状态。
这样我们就实现了django+celery+redis实现异步发邮件的功能,当然celery不仅能执行实时异步任务,还可以执行定时任务,有时间再写吧。