Bootstrap

django中使用RabbitMQ

在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。

;