轮询
假设有这么一个场景:要做一个在线投票的系统,那么我的浏览器看的的投票数量应该是动态变化的,一旦有其他人投票,那么我的浏览器显示的投票数应该会发生改变,那么这个场景应该怎么做呢?第一反应想到的就是每隔一段时间去发请求拿最新数据,比如每隔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>
这样虽然可以完成需求,但是还存在几个问题:
- 实时性没法保证,因为每隔2s才拿数据,即使在第1s的时候有人投票了,那么也要再过1s才能拿到数据
- 为了实时性更好,发送请求的时间缩短,但是这样又会明显增加服务器压力
那么,有没有这么一种方式:每隔比较长的时间去发一次请求,如果期间有人投票,那么立即返回数据,如果没有人投票,则就等待设定的那段比较长的时间请求再结束。
现在问题就转化为我去发一个请求,在这个请求里面应该监测是否有人投票,或者说监测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>