在MQ之前,我一直使用的redis作为中间人broker然后用celery执行耗时任务。从未在python项目中使用过MQ。所以今天就在django中用RabbitMQ取代redis+celery。
django中使用RabbitMQ:
本次使用RabbitMQ完成点击注册后给用户发送激活邮件的场景,如果不使用MQ,或者不使用异步的方式,后端就会一直等待smtp服务器把邮件发到用户邮箱后才往下走(我们用time.sleep(5)来模拟一下发邮件的耗时
)。由于就这一个消息类型,我们就用个简单模式,请看上一期RabbitMQ的六种模式:
1、django中的发送邮件配置
:
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' # 固定写法
EMAIL_HOST = 'smtp.qq.com' # QQ的SMTP地址,是163邮箱则换为smtp.163.com
EMAIL_PORT = 25 # SMTP端口
EMAIL_HOST_USER = '[email protected]' #发送邮件的邮箱
EMAIL_HOST_PASSWORD = 'aaaaaaaaaaaaa' # 授权码,qq邮箱中发短信后获取到的
# 发件人,<>里面的东西必须和EMAIL_HOST_USER(目的邮箱地址一摸一样,不能随便写,不然邮件发不出去)
EMAIL_FROM = '买了佛冷<[email protected]>'
2、在项目中定义一个类,把塞email进MQ的操作放在构造方法中,到时只需RabbitMQ(email)即可
:
import pika
class RabbitMQ():
def __init__(self,email):
# 1、连接rabbitmq服务器
connection = pika.BlockingConnection(pika.ConnectionParameters('x.x.x.x'))
channel = connection.channel()
# 2、创建一个名为hello的队列
channel.queue_declare(queue='hello')
# 3、简单模式,向名为hello队列中插入用户邮箱地址email
channel.basic_publish(exchange='',
routing_key='hello',
body=email,
)
print("发送用户邮箱:‘{}’ 到MQ成功".format(email))
connection.close()
3、定义一个base.py文件用于导入django环境
,离线脚本初始化用户数据的时候就需要导入django环境,我们的消费者端要使用django的send_mail函数,所以也要导入django环境。
import os
import sys
import django
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(base_dir)
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "你的项目名.settings")
django.setup() # os.environ['DJANGO_SETTINGS_MODULE']
4、编写消费者端代码
:
消费者端监听hello队列,在接收到用户的email后,执行回调callback(发邮件操作),回调中模拟了发邮件的等待时间time.sleep(5)
。稍后前端点击注册的时候,观察是否为立即跳转,如果立即跳转就说明MQ是异步,只管塞消息不管处理和处理结果。
# base.py文件用于导入django环境,离线脚本初始化用户数据的时候就需要导入django环境
import base
import pika
import time
from django.core.mail import send_mail
from django_MQ_demo import settings
# 1、连接rabbitmq服务器
connection = pika.BlockingConnection(pika.ConnectionParameters(host='x.x.x.x'))
channel = connection.channel()
# 2、创建队列,可以只让一方创建,主要是分不清哪边先跑起来,所以这儿也要创建同样的队列
channel.queue_declare(queue='hello')
# 3、构建回调函数
def callback(ch,method,properties,email):
print("消费者端收到用户邮箱 ‘{}’ 成功".format(email))
subject = 'lanyc注册'
message = ''
sender = settings.EMAIL_FROM
receiver = [email]
# 讲道理还应该来一个html_message,内容为一个激活链接的a标签
# send_mail是django内置的发邮件函数,这四个是必须参数
send_mail(subject, message, sender, receiver)
time.sleep(5)
print('向目的邮箱{}发送邮件成功'.format(email))
# 发送应答信号,表明数据已经处理完成,可以删除,和auto_ack=False套起来用
ch.basic_ack(delivery_tag=method.delivery_tag)
# 确定监听队列:hello,一旦有值出现,则触发回调函数:callback
channel.basic_consume(queue='hello',
auto_ack=False,
on_message_callback=callback,
)
print('当前MQ简单模式正在等待生产者往消息队列塞消息.......要退出请按 CTRL+C.......')
channel.start_consuming()
5、前端代码
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注 册</title>
</head>
<body>
<form id="registerForm">
{% csrf_token %}
{% for field in form %}
{{ field.label }}:{{ field }}<br>
{% endfor %}
<input type="button" id='regForm' value="注 册">
</form>
</body>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
<script>
$(function () {
registerFunc();
});
function registerFunc() {
//注册按钮绑定
$('#regForm').click(function () {
$.ajax({
url:'/email',
method:'post',
dataType:'json',
//带上用户提交的所有表单信息
data:$('#registerForm').serialize(),
success: function (res) {
if (res.status){
alert('注册成功!点击确定跳转到邮箱登录页面,请点击您的邮件完成激活');
location.href=res.data;
}else{
alert("注册失败")
}
}
})
})
}
</script>
</html>
6、django后端代码
(还要配路由):
# 路由:/email
class UserModelForm(ModelForm):
class Meta:
model = UserInfo
fields = "__all__"
def register_email(request):
if request.method == "GET":
# 要写个form类
form = UserModelForm()
return render(request,'register.html',{'form':form})
if request.method == "POST":
email = request.POST.get('email')
# 实例化上面定义的生产者方的类,自动执行其构造方法,塞email进队列
RabbitMQ(email)
return JsonResponse({'status':True,'data':'https://mail.163.com/'})
return JsonResponse({'status':False})
先运行消费者端代码:
后端的form类中这两个字段我都没写校验钩子只用来生成表单。所以不填用户名没报错,这不是重点。重点是我点击注册后,ajax的post请求会走到后端的RabbitMQ(email)
这行代码,执行类中自定义的构造方法塞email进消息队列,塞完email马上返回JsonResponse,不会去等消费者中回调函数的time.sleep(5)
模拟时间以及真实发邮件花费的时间。所以页面效果是马上弹出模态框。
163邮箱收到邮件:
消费者端控制台:
这就是整个django中使用rabbitmq,整个过程就是实现了异步,解耦。后端发邮件只需塞email进MQ,然后就return。如果在后端中直接调用发邮件函数send_mail,就要等着邮件发出去才会return。