长短轮询

长短轮询

长轮询 Long polling 阶段(Comet Long polling)

原理:服务端给每个客户端建立队列,让浏览器通过ajax向后端偷偷的发送请求,去各自对应的队列中获取数据,如果没有数据会阻塞,但是不会一直阻塞,会通过timeout参数及异常处理的方式限制阻塞事件,比如30s后返回客户端触发回调函数让浏览器再次发送请求。

image-20211106173700423

长轮询是对轮询的改进版,客户端发送 HTTP 给服务器之后,有没有新消息,如果没有新消息,就一直等待。直到有消息或者超时了,才会返回给客户端。消息返回后,客户端再次建立连接,如此反复。这种做法在某种程度上减小了网络带宽和 CPU 利用率等问题。

这种方式也有一定的弊端,实时性不高。如果是高实时的系统,肯定不会采用这种办法。因为一个 GET 请求来回需要 2个 RTT,很可能在这段时间内,数据变化很大,客户端拿到的数据已经延后很多了。

另外,网络带宽低利用率的问题也没有从根源上解决。每个 Request 都会带相同的 Header。

对应的,Web 也有 AJAX 长轮询,也叫 XHR 长轮询。

客户端打开一个到服务器端的 AJAX 请求,然后等待响应,服务器端需要一些特定的功能来允许请求被挂起,只要一有事件发生,服务器端就会在挂起的请求中送回响应并关闭该请求。客户端在处理完服务器返回的信息后,再次发出请求,重新建立连接,如此循环。

img

  • 优点:减少轮询次数,低延迟,浏览器兼容性较好。
  • 缺点:服务器需要保持大量连接。
  • 相对于轮询优缺点:
  1. 消息基本没有延迟
  2. 请求次数减少了
  3. 消耗资源较少
  4. 现在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)
# 大公司一般情况下都会使用上面长轮询的方式,因为兼容性好

image-20210821154018941

posted @ 2021-11-06 17:40  RandySun  阅读(223)  评论(0编辑  收藏  举报