在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 = 'aaaaaa@qq.com' #發送郵件的郵箱 EMAIL_HOST_PASSWORD = 'aaaaaaaaaaaaa' # 授權碼,qq郵箱中發短信后獲取到的 # 發件人,<>里面的東西必須和EMAIL_HOST_USER(目的郵箱地址一摸一樣,不能隨便寫,不然郵件發不出去) EMAIL_FROM = '買了佛冷<aaaaaa@qq.com>'
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) # 1、將用戶提交的郵箱地址傳入RabbitMQ隊列------->2、消費端一直等待,就直接取過該隊列消息,處理,給用戶發送消息
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。
文章來源:https://blog.csdn.net/qq_37623764/article/details/105825554