輪詢、長輪詢、websock

引入

 Web端即時通信技術:即時通信技術簡單的說就是實現這樣一種功能:服務器端能夠即時地將數據的更新或變化反應到客戶端,例如消息即時推送等功能都是經過這種技術實現的。可是在Web中,因爲瀏覽器的限制,實現即時通信須要藉助一些方法。這種限制出現的主要緣由是,通常的Web通訊都是瀏覽器先發送請求到服務器,服務器再進行響應完成數據的現實更新。css

  實現Web端即時通信的方法:實現即時通信主要有四種方式,它們分別是輪詢、長輪詢(comet)、長鏈接(SSE)、WebSocket。它們大致能夠分爲兩類,一種是在HTTP基礎上實現的,包括短輪詢、comet和SSE;另外一種不是在HTTP基礎上實現是,即WebSocket。下面分別介紹一下這四種輪詢方式,以及它們各自的優缺點。html

 

當咱們要實現一個實時投票系統,或者是實時通信系統,咱們的頁面數據總須要更新前端

咱們不能讓用戶一直去刷新頁面。因此就有了輪詢,長輪詢,以及websock的出現vue

 

輪詢

既然我想要實時獲取後端的數據,那我就每隔2秒給後端發一次請求python

這種咱們就叫輪詢~那它會有一些缺點就是存在延時~就算每秒發一次~也會存在必定的延遲ios

下面咱們看下輪詢的代碼:web

from flask import Flask, render_template, request, jsonify

app = Flask(__name__)

USERS = {
    1: {"name": "悟空", "count": 0},
    2: {"name": "悟能", "count": 0},
    3: {"name": "悟淨", "count": 0},

}


@app.route("/")
def index():
    return render_template("index.html", users=USERS)


@app.route("/vote", methods=["POST"])
def vote():
    uid = request.json.get("uid")
    USERS[uid]["count"] += 1
    return "投票成功"


@app.route("/get_vote")
def get_vote():
    return jsonify(USERS)


if __name__ == '__main__':
    app.run()
app.py
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.js"></script>



</head>
<body>
<h1>選醜大賽</h1>

<ul>
    {% for key, value in users.items()%}
        <li id="{{key}}" onclick="vote({{key}})">{{value.name}} ({{value.count}})</li>
    {% endfor %}
</ul>

<script>
    function vote(uid) {
        axios.request({
            url: "/vote",
            method: "POST",
            data: {
                "uid": uid
            }
        }).then(function (res) {
            console.log(res.data)
        })
    }

    function get_vote() {
        axios.request({
            url: "/get_vote",
            method: "GET"
        }).then(function (res) {
             console.log(res)
            for(let key in res.data){
                 let liEle = document.getElementById(key);
                 let username = res.data[key]["name"]
                 let count = res.data[key]["count"]
                 liEle.innerText = `${username} (${count})`
            }
        })
    }

    window.onload = function () {
        setInterval(get_vote, 2000)
    }

</script>

</body>
</html>
index.html

 

長輪詢

輪詢缺點就是延遲,那麼若是前端發送過來請求,若是沒有數據的更新npm

後端的請求就阻塞了,直到有數據返回或者超時再返回,這樣延遲就能夠獲得很好的解決json

python中有個queue對象,當咱們從這個隊列裏拿不到值的時候,能夠阻塞住請求的flask

import queue
from flask import Flask

app = Flask(__name__)
q = queue.Queue()


@app.route("/get")
def index():
    try:
        val = q.get(timeout=20)
    except queue.Empty:
        val = "超時"
    return val


@app.route("/vote")
def vote():
    q.put("10")
    return "投票成功"



if __name__ == '__main__':
    app.run()
queueDemo.py

若是我爲每一個請求都創建一個q對象,而後阻塞住他們的請求,有數據更新的時候,給他們的q對象返回值就能夠了。

from flask import Flask, render_template, request, jsonify, session
import queue
import uuid

app = Flask(__name__)
app.secret_key = "lajdgia"


USERS = {
    1: {"name": "悟空", "count": 0},
    2: {"name": "悟能", "count": 0},
    3: {"name": "悟淨", "count": 0},

}
# 爲每一個用戶創建一個q對象
# 以用戶的uuid爲key 值爲q對象
Q_DICT = {}


@app.route("/")
def index():
    user_uuid = str(uuid.uuid4())
    session["user_uuid"] = user_uuid
    Q_DICT[user_uuid] = queue.Queue()
    return render_template("index2.html", users=USERS)


@app.route("/vote", methods=["POST"])
def vote():
    # 投票 循環q對象的dict 給每一個q對象返回值
    uid = request.json.get("uid")
    USERS[uid]["count"] += 1
    for q in Q_DICT.values():
        q.put(USERS)
    return "投票成功"


@app.route("/get_vote", methods=["POST", "GET"])
def get_vote():
    # 獲取投票結果 去本身的q對象裏取值 沒有夯住 知道有或者超時返回
    user_uuid = session.get("user_uuid")
    q = Q_DICT[user_uuid]
    try:
        users = q.get(timeout=30)
    except queue.Empty:
        users = ""
    return jsonify(users)


if __name__ == '__main__':
    app.run()
app2.py
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.js"></script>



</head>
<body>
<h1>選醜大賽</h1>

<ul>
    {% for key, value in users.items()%}
        <li id="{{key}}" onclick="vote({{key}})">{{value.name}} ({{value.count}})</li>
    {% endfor %}
</ul>

<script>
    function vote(uid) {
        axios.request({
            url: "/vote",
            method: "POST",
            data: {
                "uid": uid
            }
        }).then(function (res) {
            console.log(res.data)
        })
    }

    function get_votes() {
        axios.request({
            url: "/get_vote",
            method: "POST"
        }).then(function (res) {
             console.log(res);
            if(res.data != ""){
                 for(let key in res.data){
                 let liEle = document.getElementById(key);
                 let username = res.data[key]["name"]
                 let count = res.data[key]["count"]
                 liEle.innerText = `${username} (${count})`
                }
            }
            get_votes()
        })
    }

    window.onload = function () {
        get_votes()
    }

</script>

</body>
</html>
index2.html

 

websocket

websocket是一個協議,協議規定

鏈接的時候須要握手,發送的數據須要加密~~鏈接以後不斷開

Flask不帶websocket,咱們須要下載

下載:pip install gevent-websocket

from flask import Flask, request, render_template
from geventwebsocket.handler import WebSocketHandler
from gevent.pywsgi import WSGIServer
import json


app = Flask(__name__)

USERS = {
    1: {"name": "悟空", "count": 0},
    2: {"name": "悟能", "count": 0},
    3: {"name": "悟淨", "count": 0},

}


@app.route("/")
def index():
    return render_template("index3.html", users=USERS)


WEBSOCKET_LIST = []
@app.route("/vote")
def vote():
    ws = request.environ.get("wsgi.websocket")
    if not ws:
        return "HTTP請求"
    WEBSOCKET_LIST.append(ws)
    while True:
        uid = ws.receive()
        if not uid:
            WEBSOCKET_LIST.remove(ws)
            ws.close()
            break
        uid = int(uid)
        USERS[uid]["count"] += 1
        name = USERS[uid]["name"]
        new_count = USERS[uid]["count"]
        for client in WEBSOCKET_LIST:
            client.send(json.dumps({"uid": uid, "name": name, "count": new_count}))


if __name__ == '__main__':
    http_server = WSGIServer(('127.0.0.1', 5000), app, handler_class=WebSocketHandler)
    http_server.serve_forever()
app3.py
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Title</title>
    <script src="https://cdn.bootcss.com/axios/0.19.0-beta.1/axios.js"></script>


</head>
<body>
<h1>選醜大賽</h1>

<ul>
        {% for (key, value) in users.items() %}
            <li onclick="vote({{key}})" id="{{key}}">{{value.name}} ({{value.count}})</li>
        {% endfor%}

</ul>

<script>
    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>
index3.html
相關文章
相關標籤/搜索