web长轮询

轮询

假设有这么一个场景:要做一个在线投票的系统,那么我的浏览器看的的投票数量应该是动态变化的,一旦有其他人投票,那么我的浏览器显示的投票数应该会发生改变,那么这个场景应该怎么做呢?第一反应想到的就是每隔一段时间去发请求拿最新数据,比如每隔2s通过ajax偷偷发请求去获取最新票数然后渲染到我的浏览器。
flask_test.py

from flask import Flask, render_template, jsonify, request

app = Flask(__name__)
app.deug = True

VOTE_LIST = [
    {'name': '小明', 'count':3, 'id':1},
    {'name': '小红', 'count':5, 'id':2},
    {'name': '小花', 'count':6, 'id':3}
]

@app.route('/index')
def index():
    return render_template('index.html')

@app.route('/get_vote')
def get_vote():
    return jsonify(VOTE_LIST)

@app.route('/vote/')
def vote():
    nid = request.args.get('nid')
    for item in VOTE_LIST:
        if item['id'] == int(nid):
            item['count'] += 1
    return 'vote success'


if __name__ == '__main__':
    app.run(host='0.0.0.0')

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .box1{
            width: 100px;
            height: 100px;
            background-color: red;
        }
        .box2{
            width: 100px;
            height: 100px;
            background-color: green;
        }
    </style>

</head>
<body>
    <div id="app">
        <ul v-for="item in vote_list">
            <li>{[ item.name ]}({[ item.count ]})</li>
        </ul>
    </div>



    <script src="/static/jquery-3.3.1.min.js"></script>
    <script src="/static/vue.min.js"></script>
    <script>
        var app = new Vue({
            el: '#app',
            delimiters:['{[', ']}'],
            data: {
                vote_list:[],
                cl:null
            },
            created() {
                var that = this;
                this.cl = setInterval(function () {
                    $.ajax({
                        url: '/get_vote',
                        type: 'get',
                        success: function (arg) {
                            console.log(arg)
                            that.vote_list = arg
                        }
                    })
                }, 2000)
            }

        });


    </script>
</body>
</html>

这样虽然可以完成需求,但是还存在几个问题:

  1. 实时性没法保证,因为每隔2s才拿数据,即使在第1s的时候有人投票了,那么也要再过1s才能拿到数据
  2. 为了实时性更好,发送请求的时间缩短,但是这样又会明显增加服务器压力

那么,有没有这么一种方式:每隔比较长的时间去发一次请求,如果期间有人投票,那么立即返回数据,如果没有人投票,则就等待设定的那段比较长的时间请求再结束。
现在问题就转化为我去发一个请求,在这个请求里面应该监测是否有人投票,或者说监测vote_list是否有变化,有变化就立即返回。那怎么去监测呢?写一个死循环去监测一个变量的内容是否变化?当然这样肯定是可以的,但是比较low,在这里可以借助队列来实现。我们把这种方式称之为长轮询

长轮询

flask_test.py

import uuid

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

app = Flask(__name__)
app.deug = True
app.secret_key = 'asdasdas'

VOTE_LIST = [
    {'name': '小明', 'count':3, 'id':1},
    {'name': '小红', 'count':5, 'id':2},
    {'name': '小花', 'count':6, 'id':3}
]

QUEUE_DICT = {}

@app.route('/index')
def index():
    uid = str(uuid.uuid4())
    session['uid'] = uid
    QUEUE_DICT[uid] = Queue()
    return render_template('index.html')

@app.route('/first_get_vote')
def first_get_vote():
    return jsonify(VOTE_LIST)


@app.route('/get_vote')
def get_vote():
    global VOTE_LIST
    uid = session.get('uid')
    q = QUEUE_DICT[uid]
    try:
        VOTE_LIST = q.get(timeout=5)
    except Exception:
        VOTE_LIST = VOTE_LIST
    return jsonify(VOTE_LIST)

@app.route('/vote/')
def vote():
    nid = request.args.get('nid')
    for item in VOTE_LIST:
        if item['id'] == int(nid):
            item['count'] += 1
    for q in QUEUE_DICT.values():
        q.put(VOTE_LIST)
    return 'vote success'


if __name__ == '__main__':
    # 开启多线程
    app.run(host='0.0.0.0', threaded=True)

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .box1{
            width: 100px;
            height: 100px;
            background-color: red;
        }
        .box2{
            width: 100px;
            height: 100px;
            background-color: green;
        }
    </style>

</head>
<body>
    <div id="app">
        <ul v-for="item in vote_list">
            <li>{[ item.name ]}({[ item.count ]})</li>
        </ul>
    </div>



    <script src="/static/jquery-3.3.1.min.js"></script>
    <script src="/static/vue.min.js"></script>
    <script>
        var app = new Vue({
            el: '#app',
            delimiters:['{[', ']}'],
            data: {
                vote_list:[],
                cl:null
            },
            methods:{
                get_vote(){
                     var that = this;
                    $.ajax({
                        url: '/get_vote',
                        type: 'get',
                        success: function (arg) {
                            console.log(arg)
                            that.vote_list = arg
                            // 在函数里面调用函数自己,递归
                            that.get_vote();
                        }
                    })
                }
            },

            created() {
                var that = this;
                $.ajax({
                        url: '/first_get_vote',
                        type: 'get',
                        success: function (arg) {
                            that.vote_list = arg
                        }
                    })
                this.get_vote()
            }

        });


    </script>
</body>
</html>

posted @ 2018-08-17 10:04  龙云飞谷  阅读(842)  评论(0编辑  收藏  举报