服務端主動給客戶端推送消息
如何實現(目錄)
- 輪詢
- 長輪詢
- websocket
應用場景:大屏幕實時投票、任務執行的流程...
飯前甜點
-
ajax操作
異步提交,局部刷新。用它就可以實現我們上面的輪詢/長輪詢
$.ajax({ url:'', # 后端服務器地址 type:'', # 請求方式 data:{}, # 發送的數據 dataType:'JSON', # 如果django后端是通過HttpResponse返回數據的那么不會自動返序列化,而如果是JsonResponse則會自動轉化,該參數可以不指定 success:function(args){ # 執行成功之后需要進行的操作 異步回調 } }) return HttpResponse(json.dumps(back_dic)) # 需要dataType參數 return JsonResponse(back_dic) # 不需要
補充:templates模版文件夾可以放在全局,也可以在每一個應用下創建該文件夾
如果全局沒有,那么在查找模版的時候就會去應用下查找模版,順序是按照配置文件中注冊了的app的順序依次查找
-
隊列
隊列:先進先出
堆棧:先進后出
import queue q = queue.Queue() # 創建一個隊列 q.put('jason') # 朝隊列中丟數據 q.put('egon') v1 = q.get() # 獲取數據 v2 = q.get() # v3 = q.get() # 如果沒有數據了,get方法默認會一直阻塞 try: v3 = q.get(timeout=3) # 等3秒 之后還沒數據直接報錯 queue.Empty except queue.Empty as e: print(e) print(v1,v2) # 注意該隊列並不會用於實際生產 主要是用來做本地的測試 # 實際生產建議使用redis、kafka、rebittMQ
基於隊列和ajax我們來設想:實現群聊功能
pending狀態
-
遞歸
# python最大遞歸深度多少呀 997、998、... 官網說是1000 def func(): func() func() # 在js中 根本沒有遞歸的概念 函數內部自己調用自己是可以的 屬於正常事件范疇 function func1(){ $.ajax({ url:'', type:'get', data:{}, success:function(args){ func1() } }) } $(function(){ # 等待頁面加載完畢之后執行函數內的代碼 func1() })
-
modelform
forms組件的加強版
自動校驗數據、渲染標簽、展示錯誤信息
服務端朝客戶端主動推送消息
-
輪詢
效率低、基本不用
讓瀏覽器定時朝后端發送請求(通過ajax向后端偷偷發送數據),比如每隔五秒鍾發一次請求,那么你的數據延遲就可能會高達五秒 不足之處 數據延遲 消耗資源過大 請求次數太多
-
長輪詢
兼容性好
一般大公司都會考慮使用它
# 隊列+ajax 服務端給每個客戶端建立隊列,讓瀏覽器通過ajax朝服務端要數據,去各自的隊列中獲取 如果沒有數據則會阻塞但是不會一直阻塞,比如阻塞你30秒,還沒有數據則返回,然后讓客戶端瀏覽器再次發送請求數據的請求 相對於輪詢 基本是沒有消息延遲的 請求次數降低了很多 # web版本的qq和微信基本上用的都是這么一個邏輯
基於ajax及隊列實現長輪詢的功能
""" 1.首頁自定義用戶唯一表示,給每個用戶初始化一個隊列 2.發送按鈕綁定點擊事件 后端講數據放入每一個隊列中 3.書寫自動獲取數據的ajax代碼 循環調用 4.前端獲取數據DOM操作渲染頁面 """ $('#d1').click(function () { $.ajax({ url:'/send_msg/', type:'post', data:{'content':$('#d2').val()}, dataType:'JSON', success:function (args) { } }) }); function getMsg(){ $.ajax({ url:'/get_msg/', type:'get', data:{'name':'{{ name }}'}, // 只要當前登陸人的隊列中的數據 {#dataType:'JSON',#} success:function (args) { // 針對返回的消息做相應的處理 if(args.status){ // 有消息則渲染頁面 講消息全局放到聊天紀錄里面 // 1 創建標簽 var pEle = $('<p>'); // 2 給標簽設置文本內容 pEle.text(args.msg); // 3 講創建好的標簽添加到聊天記錄div標簽內 $('#content').append(pEle) }else{ // 沒有消息 則繼續發送 } getMsg() // 循環請求數據 } }) } $(function () { getMsg() // 等待頁面加載完畢自動執行 }) # 后端 # 全局大字典 q_dict = {} # {'唯一表示':隊列,....} def ab_bl(request): # 獲取我們自定義的客戶端唯一標識 name = request.GET.get('name') # 給每一個客戶端創建一個隊列 q_dict[name] = queue.Queue() return render(request,'ab_bl.html',locals()) def send_msg(request): if request.method == 'POST': # 獲取用戶發送的消息 content = request.POST.get('content') # 講該消息傳遞給所有的隊列 for q in q_dict.values(): q.put(content) return HttpResponse('OK') def get_msg(request): name = request.GET.get('name') # 拿到對應的隊列 q = q_dict.get(name) # 講隊列中可能有的數據取出並返回給前端瀏覽器 # 定義一個字典與ajax進行交互 back_dic = {'status':True,'msg':''} try: data = q.get(timeout=10) # 等10s 沒有則直接報錯 back_dic['msg'] = data except queue.Empty as e: back_dic['status'] = False return JsonResponse(back_dic) # return HttpResponse(json.dumps(back_dic))
-
websocket
真正的做到服務端發送消息而不再是被動的發送
目前主流的瀏覽器都是支持websocket
""" HTTP協議 網絡協議(不加密傳輸) HTTPS協議 網絡協議(加密傳輸) 上面兩個協議都是短鏈接 websocket網絡協議 (加密傳輸) 瀏覽器和服務端創建鏈接之后 默認不再斷開 兩端都可以基於該鏈接收發消息 websocket的誕生能夠真正做到服務端發送消息而不再是被動的發送 """
websocket內部原理(了解 面試可以說關鍵點)
""" 分成兩大部分 1.握手環節:驗證服務端是否支持websocket協議 先連接服務器 瀏覽器產生一個隨機字符串 給服務端發送一份(請求頭) 自己留一份 Sec-WebSocket-Key: ePW8kp1XqLNWbJxE/Q38SA== 服務端和客戶端都對隨機字符串做下面的操作 隨機字符串 + magic string拼接 然后再講拼接好的結果進行加密處理(sha1/base64)的到密文 瀏覽器自動比對雙方產生的密文是否一致,如果一致說明服務端支持websocket 如果不一致會報錯 假設比對上了 建立websocket鏈接 基於該鏈接收發消息 2.收發數據 密文傳輸 >>> 必然要涉及解密(全球統一)的過程 基於網絡傳輸的數據都是二進制格式 對應到我們python中就是bytes類型 詳見qq群里面的截圖 數據解密過程 1.先讀取數據的第二個字節的后7位(payload) 根據7位數據的大小來指定不同的解密流程 =127:再往后讀取8個字節 =126:再往后讀取2個字節 <=125:不再往后讀取 除去前面讀取的數據之外 再往后讀4個字節(masking-key) 拿着它去解析后面的真實數據(依據一個計算公式)