一、輪詢
在一些需要進行實時查詢的場景下應用
比如投票系統:
大家一起在一個頁面上投票
在不刷新頁面的情況下,實時查看投票結果
1、后端代碼
from flask import Flask, render_template, request, jsonify app = Flask(__name__) USERS = { 1: {'name': '小米', 'count': 300}, 2: {'name': '小康', 'count': 200}, 3: {'name': '小明', 'count': 600}, } @app.route('/') def index(): return render_template('Poll.html', users=USERS) @app.route('/vote', methods=['POST']) def vote(): # 接收uid,通過uid給票數 +1 # 用戶提交Json數據過來,用request.json獲取 uid = request.json.get('uid') USERS[uid]['count'] += 1 return "投票成功" @app.route('/get_vote') def get_vote(): # 返回users數據 # jsonify 是flask自帶的序列化器 return jsonify(USERS) if __name__ == '__main__': app.run()
2、前端代碼
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>投票系統</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"> <script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.js"></script> </head> <style> .my-li { list-style: none; margin-bottom: 20px; font-size: 18px; } </style> <body> <div class="container"> <div class="row"> <div class="col-md-6 col-md-offset-3"> <h1>最帥男人投票</h1> {% for (uid, user) in users.items() %} <button class="btn btn-success" onclick="vote({{ uid }})">投票</button> <li class="list-group-item-info my-li" id="{{ uid }}">{{ user.name }}目前的票數是: {{ user.count }}</li> {% endfor %} </div> </div> </div> <script> // 投票 function vote(uid) { // 向后端發送投票請求 axios.request({ url: '/vote', method: 'post', data: { uid: uid } }).then(function (response) { console.log(response.data); }) } // 獲取最新的投票結果 function get_vote() { axios.request({ url: '/get_vote', method: 'get' }).then(function (response) { // 獲取后端傳過來的新數據 // 重新渲染頁面 let users = response.data; for (let uid in users) { //根據uid獲取li標簽 改變innerText let liEle = document.getElementById(uid); liEle.innerText = `${users[uid]['name']}目前的票數是: ${users[uid]['count']}` } }); } // 頁面加載完后,立刻獲取數據 window.onload = function () { setInterval(get_vote, 2000) } </script> </body> </html>
3、輪詢
特點:每隔一段時間不斷向后端發送請求
缺點:消耗大 有延遲
二、長輪詢
由於上面的輪詢是不能實時查看到投票情況的,存在一定的延遲性
長輪詢可以實現實時查看投票情況
1、后端代碼
from flask import Flask, render_template, request, jsonify, session import uuid import queue app = Flask(__name__) app.secret_key = '切克鬧' USERS = { 1: {'name': '小米', 'count': 300}, 2: {'name': '小康', 'count': 200}, 3: {'name': '小明', 'count': 600}, } Q_DICT = { # uid: q對象 } @app.route('/') def index(): # 模擬用戶登錄 # 模擬用戶登錄后的唯一id user_id = str(uuid.uuid4()) # 每個用戶都有自己的Q對象 Q_DICT[user_id] = queue.Queue() # 把用戶的id存到session session['user_id'] = user_id # 頁面展示投票的人的信息 return render_template('longPoll.html', users=USERS) @app.route('/vote', methods=['POST']) def vote(): # 處理投票,給票數 +1 # 用戶提交Json數據過來,用request.json獲取 uid = request.json.get('uid') USERS[uid]['count'] += 1 # 投票成功后,給每個用戶的Q對象put最新的值進去 for q in Q_DICT.values(): q.put(USERS) return "投票成功" @app.route('/get_vote') def get_vote(): # 請求進來,從session獲取用戶的id user_id = session.get('user_id') # 根據用戶的id 獲取用戶的Q對象 q = Q_DICT.get(user_id) # try: # ret = q.get(timeout=30) # except queue.Empty: # ret = '' ret = q.get() return jsonify(ret) if __name__ == '__main__': app.run()
2、前端代碼
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>投票系統</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"> <script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.js"></script> </head> <style> .my-li { list-style: none; margin-bottom: 20px; font-size: 18px; } </style> <body> <div class="container"> <div class="row"> <div class="col-md-6 col-md-offset-3"> <h1>最曬男人投票</h1> {% for (uid, user) in users.items() %} <button class="btn btn-success" onclick="vote({{ uid }})">投票</button> <li class="list-group-item-info my-li" id="{{ uid }}">{{ user.name }}目前的票數是: {{ user.count }}</li> {% endfor %} </div> </div> </div> <script> // 投票 function vote(uid) { // 向后端發送投票請求 axios.request({ url: '/vote', method: 'post', data: { uid: uid } }).then(function (response) { console.log(response.data); }) } // 獲取最新的投票結果 function get_vote() { axios.request({ url: '/get_vote', method: 'get' }).then(function (response) { // 判斷后端的數據是否為空 if (response.data != '') { // 獲取到最新的數據 let users = response.data; for (uid in users) { // 根據uid找到每個li標簽 let liEle = document.getElementById(uid); // 給每個li標簽設置最新的數據 liEle.innerText = `${users[uid]['name']}目前的票數是: ${users[uid]['count']}` } } // 獲取完數據后,再發送請求,看還有沒有人投票,有的話再去獲取最新的數據 get_vote() }); } // 頁面加載完后,立刻獲取數據 window.onload = function () { get_vote() } </script> </body> </html>
3、長輪詢
特點:滿足實時更新
缺點:消耗大
實現:
利用queue對象實現請求夯住
每個請求進來都要生成一個q對象
如果有人投票 給所有的q對象put數據
拿數據請求從自己的q對象get數據
三、websocket
1、介紹
http協議
短連接 無狀態 基於TCP/UDP協議進行傳輸數據(TCP/UDP: 傳輸協議)
socket
socket不是傳輸協議 跟websocket是兩個完全不一樣的東西 socket是套接字 API接口
websocket
H5出的新協議 瀏覽器和服務器只需要完成一次握手,兩者之間就直接可以創建持久性的連接,並進行雙向數據傳輸
解決輪詢問題
特點:
1. 握手 基於HTTP進行握手(因此websocket與Http有一定的交集,但不是同一個東西)
2. 發送數據加密
3. 保持連接不斷開
2、在Flask中使用websocket
1. Flask沒有websocket,需要安裝包 pip install gevent-websocket 2. 后端怎樣建立一個支持websocket協議連接 from geventwebsocket.handler import WebSocketHandler from gevent.pywsgi import WSGIServer # 拿到websocket對象 ws = request.environ.get("wsgi.websocket") # 后端發送數據 ws.send(xxx) # 后端接收數據 ws.receive() if __name__ == '__main__': # app.run() # 即支持HTTP 也支持websocket http_server = WSGIServer(('0.0.0.0', 5000), app, handler_class=WebSocketHandler) http_server.serve_forever() 3. 前端怎么發起websocket連接 let ws = new WebSocket("ws://127.0.0.1:5000") # 前端發送數據 ws.send("xxx") # 前端接收數據 ws.onmessage = function(event){ # 從接受的對象中獲取數據 let data = event.data } 4. 收發消息 1, 前端發送數據給后端 前端發送:ws.send('數據') 后端接收:ws.receive() 2, 后端發送數據給前端 后端發送:ws.send('數據') 前端接收的是對象:ws.onmessage = function(event){ let data = event.data }
3、Demo
1.后端
from flask import Flask, render_template, request from geventwebsocket.handler import WebSocketHandler from gevent.pywsgi import WSGIServer import json app = Flask(__name__) USERS = { 1: {'name': '小米', 'count': 300}, 2: {'name': '小康', 'count': 200}, 3: {'name': '小明', 'count': 600}, } WEBSOCKET_LIST = [] @app.route('/') def index(): return render_template('websocket.html', users=USERS) @app.route('/vote') def vote(): # 處理websocket # 判斷是什么類型的請求,HTTP還是websocket # 看能否獲取得到websocket的對象 ws = request.environ.get("wsgi.websocket") if not ws: return "這是HTTP協議的請求" # 把所有用戶的ws對象存到一個列表 WEBSOCKET_LIST.append(ws) while True: # 獲取前端傳過來的uid,給打野票數 +1 uid = ws.receive() # 如果前端主動斷開連接 # 那么后端也關閉與前端的連接 if not uid: WEBSOCKET_LIST.remove(ws) ws.close() break uid = int(uid) USERS[uid]["count"] += 1 data = { "uid": uid, "name": USERS[uid]["name"], "count": USERS[uid]["count"] } for ws in WEBSOCKET_LIST: # 給前端發送新的數據 ws.send(json.dumps(data)) if __name__ == '__main__': # app.run() # 這樣啟服務的意思是:即支持HTTP協議,也支持websocket協議 http_server = WSGIServer(('127.0.0.1', 5000), app, handler_class=WebSocketHandler) http_server.serve_forever()
2.前端
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>投票系統</title> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css"> <script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.js"></script> </head> <style> .my-li { list-style: none; margin-bottom: 20px; font-size: 18px; } </style> <body> <div class="container"> <div class="row"> <div class="col-md-6 col-md-offset-3"> <h1>最曬男人投票</h1> {% for (uid, user) in users.items() %} <button class="btn btn-success" onclick="vote({{ uid }})">投票</button> <li class="list-group-item-info my-li" id="{{ uid }}">{{ user.name }}目前的票數是: {{ user.count }}</li> {% endfor %} </div> </div> </div> <script> // 向后端發送一個websocket連接請求 let ws = new WebSocket('ws://127.0.0.1:5000/vote'); function vote(uid) { // 向后端發數據 ws.send(uid) } ws.onmessage = function (event) { let data = JSON.parse(event.data); let liEle = document.getElementById(data.uid); liEle.innerText = `${data.name}目前的票數是: ${data.count}` } </script> </body> </html>