长短轮询
长短轮询
长轮询 Long polling 阶段(Comet Long polling)
原理:服务端给每个客户端建立队列,让浏览器通过ajax向后端偷偷的发送请求,去各自对应的队列中获取数据,如果没有数据会阻塞,但是不会一直阻塞,会通过timeout参数及异常处理的方式限制阻塞事件,比如30s后返回客户端触发回调函数让浏览器再次发送请求。
长轮询是对轮询的改进版,客户端发送 HTTP 给服务器之后,有没有新消息,如果没有新消息,就一直等待。直到有消息或者超时了,才会返回给客户端。消息返回后,客户端再次建立连接,如此反复。这种做法在某种程度上减小了网络带宽和 CPU 利用率等问题。
这种方式也有一定的弊端,实时性不高。如果是高实时的系统,肯定不会采用这种办法。因为一个 GET 请求来回需要 2个 RTT,很可能在这段时间内,数据变化很大,客户端拿到的数据已经延后很多了。
另外,网络带宽低利用率的问题也没有从根源上解决。每个 Request 都会带相同的 Header。
对应的,Web 也有 AJAX 长轮询,也叫 XHR 长轮询。
客户端打开一个到服务器端的 AJAX 请求,然后等待响应,服务器端需要一些特定的功能来允许请求被挂起,只要一有事件发生,服务器端就会在挂起的请求中送回响应并关闭该请求。客户端在处理完服务器返回的信息后,再次发出请求,重新建立连接,如此循环。
- 优点:减少轮询次数,低延迟,浏览器兼容性较好。
- 缺点:服务器需要保持大量连接。
- 相对于轮询优缺点:
- 消息基本没有延迟
- 请求次数减少了
- 消耗资源较少
- 现在web版本的qq和微信还是基于长轮询实现的(大公司web项目可能都会使用)
基于ajax及队列自己实现简易版本的长轮询群聊功能
django中的应用,可以有自己的urls.py,static静态文件夹,templates模版文件夹
前端:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<link href="https://cdn.bootcss.com/twitter-bootstrap/3.3.1/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.bootcss.com/twitter-bootstrap/3.3.1/js/bootstrap.min.js"></script>
</head>
<body>
<h1>聊天室: <span id="d3">{{ name }}</span></h1>
<div>
<input type="text" name="content" id="d1">
<button id="d2">发送</button>
</div>
<h1>聊天纪录</h1>
<div class="record">
</div>
<script>
// 朝后端发送用户消息
$('#d2').click(function () {
$.ajax({
url: '/send_msg/',
type: 'post',
data: {
'content': $("#d1").val(),
'name': $("#d3").text(),
'csrfmiddlewaretoken': '{{ csrf_token }}'
},
{#dataType:"JSON",#}
success: function (args) {
}
})
});
// 书写偷偷跟服务端要数据的代码
function getMsg() {
$.ajax({
url: '/get_msg/',
type: 'get',
data: {'name': '{{ name }}'},
{#dataType:"JSON",#}
success: function (args) {
if (args.status) {
// 有消息 应该DOM操作渲染到页面上
// 1 创建标签
var pEle = $('<p>');
// 2 给标签添加文本内容
pEle.text(args.msg.name +" : "+ args.msg.content);
// 3 添加到div中
$('.record').append(pEle)
} else {
// 没有则继续发送
}
getMsg()
}
})
}
// 页面加载完毕之后自动触发getMsg函数的执行
$(function () { // 等待页面加载完毕之后自动调用getMsg函数
getMsg()
})
</script>
</body>
</html>
后端:
from django.shortcuts import render
# Create your views here.
from django.shortcuts import render,HttpResponse
import queue
# Create your views here.
# 全局定义一个字典用来存储客户端浏览器与队列关系
q_dict = {}
def home(request, *args, **kwargs):
# 获取用户唯一标识
name = request.GET.get('name')
# 给每个客户端浏览器创建独有的队列
q_dict[name] = queue.Queue()
return render(request,'index.html',locals())
def send_msg(request, *args, **kwargs):
if request.method == 'POST':
con = request.POST.get('content')
name = request.POST.get('name')
# 将该消息往所有的群聊的客户端队列中添加
content = {'name': name, 'content': con, }
for q in q_dict.values():
q.put(content)
return HttpResponse('OK')
import json
from django.http import JsonResponse
def get_msg(request, *args, **kwargs):
name = request.GET.get("name")
# 去对应的队列中获取数据
q = q_dict.get(name)
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 HttpResponse(json.dumps(back_dic))
return JsonResponse(back_dic)
# 大公司一般情况下都会使用上面长轮询的方式,因为兼容性好
在当下的阶段,必将由程序员来主导,甚至比以往更甚。