【全栈开发】---- 一文掌握Django的轮询、长轮询

发布于:2025-03-06 ⋅ 阅读:(11) ⋅ 点赞:(0)

目录

引入

基础页面

轮询:

长轮询:


引入

        在我们日常开发Web应用程序时,通常依赖HTTP协议来实现客户端与服务器之间的通信。在这种模式下,浏览器发起请求,服务器处理并响应这些请求。尽管这种请求-响应模型适用于大多数应用场景,但在某些情况下,比如直播间的实时聊天功能,它显得力不从心。具体来说,在一个直播间中,当一位用户发送消息后,其他观众需要即时看到这条信息,这就要求服务器能够主动向客户端推送更新,而不是被动地等待客户端发起请求。

针对上述情况,传统上可以通过两种基于HTTP的方法来模拟服务器端的推送能力:轮询(Polling)和长轮询(Long Polling)。然而,这两种方法效率较低,因为它们涉及到频繁的HTTP连接建立和断开过程,并且难以保证消息的即时性。

为了更高效、更直接地解决这个问题,WebSocket协议提供了一个理想的解决方案。WebSocket支持全双工通信,允许服务器主动向客户端推送数据,一旦连接建立,双方即可自由地进行双向数据交换,无需每次交互都重新建立连接。这使得WebSocket成为构建如实时聊天室、在线游戏或协作工具等需要低延迟、持续连接的应用的理想选择。

因此,对于类似直播间聊天框这样的场景,采用WebSocket不仅能显著提升用户体验,通过确保所有参与者能几乎同时接收到最新的消息,而且在性能上也更加高效,减少了不必要的网络负载和延迟。随着现代Web应用对实时交互需求的增长,WebSocket已经成为实现这些高级功能不可或缺的技术之一。下面将介绍这三种方法:

基础页面

先定义一个基本的页面,可在页面发送数据给后台:

urls.py:

"""
URL configuration for poll project.

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/5.1/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from app01 import views

urlpatterns = [
    path("admin/", admin.site.urls),
    path("home/", views.home),
    path("send/msg", views.send_msg),
]

Home.html:

页面是通过 CDN 的方式加载 jquery ,当然也可以像之前那样,先下载下来,保存到静态文件中,然后引入;页面通过 Ajax 请求,将输入框输入的数据通过 GET 请求发送到特定路由,发送 GET 请求比较容易,发送 POST 请求则还得解决 CSRFtoken。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .message{
            width: 200px;
            height: 300px;
            border: 1px,solid #ddddd;

        }
    </style>
</head>
<body>
<div class="message" ></div>
<div class="input">
    <input type="text" placeholder="请输入" id="txt">
    <input type="button" value="发送" onclick="sendMessage()">
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script>
   function sendMessage(){
       var text = $("#txt").val();
       {#Ajax,使页面不刷新的情况下,将数据发送给后台#}
       $.ajax({
           {#发送url#}
           url:'/send/msg',
           type:'GET',
           {#发送数据#}
           data:{
               text:text
           },
           {#请求发送成功后,会自动执行一下这个函数#}
           success:function (res){
               console.log("请求发送成功",res)
           }
       })
   }
</script>
</body>
</html>

views.py:

创建路由后,通过 request.GET 来接收,创建一个列表充当临时数据库。

from django.http import HttpResponse
from django.shortcuts import render
# 充当数据库
DB = []

# Create your views here.
def home(request):

    return render(request, 'Home.html')

# 接收页面发送的数据
def send_msg(request):
    print("接收到客户端的请求:",end="")
    print(request.GET)
    text = request.GET['text']
    DB.append(text)
    return HttpResponse("OK")

轮询:

        轮询的大致流程是这样的,对于不同的用户,当某一个用户发送了数据给后台,后台将进行接收,同时,前端会一直发送一个请求(一般间隔1s),用于获取后端接收到的数据,获取到就显示在前端页面,没获取到就继续发送请求。

轮询的实现使用的 Ajax + setInterval

对于输入框的数据,使用 Ajax 在页面不刷新的情况下,将数据发给后台:

function sendMessage(){
       var text = $("#txt").val();
       {#Ajax,使页面不刷新的情况下,将数据发送给后台#}
       $.ajax({
           {#发送url#}
           url:'/send/msg',
           type:'GET',
           {#发送数据#}
           data:{
               text:text
           },
           {#请求发送成功后,会自动执行一下这个函数#}
           success:function (res){
               console.log("请求发送成功",res)
           }
       })
   }

在 urls.py 中,定义路径:

path("send/msg", views.send_msg),

在视图函数中,接收到数据,存入数据库中(这里用列表代替数据库)

DB = []
# 接收页面发送的数据
def send_msg(request):
    print("接收到客户端的请求:",end="")
    print(request.GET)
    text = request.GET['text']
    DB.append(text)
    return HttpResponse("OK")

此时,再发送请求到后端,请求数据,并且,请求数据只能是不重复的数据,即:假说第一次发送存储了一个“黑马”,其他用户发送请求,得到的是“黑马”;第二次发送了“程序员”,其他用户第二次发送请求,得到的应该是“程序员”,而不应该是“黑马”和“程序员”,只要请求的时候带一个最大索引参数即可。

其他用户发送请求:

{#每隔2s向后台发送请求#}
   setInterval(function (){
       {#发送请求获取数据#}
       $.ajax({
           url:'/get/msg',
           type:'GET',
           data:{
               index:max_index
           },
           success:function (dataDict){
               console.log("获取到数据:",dataDict);
               {#将这个JSON字符串转换为JavaScript对象或数组#}
               {#var dataDict = JSON.parse(res)#}
               max_index = dataDict.index
               $.each(dataDict.data,function (index,item){
                    console.log(index,item)
                   {#将内容拼接成div标签,并添加到message区域#}
                    {#document.createElement("div")  基于DOM#}
                   {#基于jquery#}
                   var tag = $("<div>");
                   tag.text(item)
                   $("#message").append(tag);
               })
           }
       })
   },2000)

这里每隔 2 s ,发送一次请求,能保证当 DB 列表有新值后,最慢也会在 2 s 后更新页面,这就是所谓的轮询,此时获取数据的视图函数:

def get_msg(request):
    index = int(request.GET.get('index'))
    print(index)
    context = {
        "data":DB[index:],
        "index":len(DB)
    }
    return JsonResponse(context)  #返回Json格式数据,序列化

        视图函数将 data 和 index 以字典形式(index 为DB的长度,用于不重复索引),用JsonResponse 返回给前端,前端用 dataDict 接收,更新 max_index 为 index,再通过 jquery 构造 div ,添加到前端页面中,实现不同用户展示数据,从而实现轮询。

长轮询:

        对于前面介绍的轮询,虽然是可行的,但是每隔 1 s 发送一次请求,会占用大量资源,导致服务器卡顿等。这时就可以使用长轮询,相较于轮询,长轮询的区别是前端发送的请求到后端,如果没有得到响应,不会立刻消失,而会等待几十秒,若在这几十秒内获取到了数据,则返回给前端,前端继续发请求;若没获取到数据,且等待响应超时,则取消前端请求与后台的连接,并重新再发送一个请求。

由于基本逻辑与轮询无异,这里展示源码及部分解释:

urls.py:

path("lang/", views.lang),
path("send/message", views.lang_send),
path("get/message", views.lang_get),

lang.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        .message{
            width: 200px;
            height: 300px;
            border: 1px solid #ddddd;
        }
    </style>
</head>
<body>
<div class="message" id="message"></div>
<div class="input">
    <input type="text" placeholder="请输入" id="txt">
    <input type="button" value="发送" onclick="sendMessage()">
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<script>
    var USER_UID = "{{ uid }}"
    function sendMessage(){
        {#.val()是取值#}
        var text = $("#txt").val();
        $.ajax({
            url:'/send/message',
            type:'GET',
            data:{
                text:text,
            },
            success:function (res){
                console.log("lang的请求发送成功",res)
            }

        })
    }
    function getMessage(){
        $.ajax({
            url:'/get/message',
            type:'GET',
            data:{
                uid:USER_UID
            },
            dataType:"JSON",
            success: function (res){
                console.log("get的请求发送成功",res)
                if(res.status){
                    var tag = $("<div>");
                    tag.text(res.data);
                    $("#message").append(tag);
                }
                {#不管是得到了数据或者请求超时,都递归调用#}
                getMessage();
            }
        })
    }
    $(function (){
        getMessage();
    })
</script>
</body>
</html>

views.py:

from django.http import HttpResponse,JsonResponse
from django.shortcuts import render
import json
import queue
from shapely.lib import length

# 充当数据库
DB = []
USER_QUEUE = {}
max_num = 0
# Create your views here.
def home(request):

    return render(request, 'Home.html')

# 接收页面发送的数据
def send_msg(request):
    print("接收到客户端的请求:",end="")
    print(request.GET)
    text = request.GET['text']
    DB.append(text)
    return HttpResponse("OK")

def get_msg(request):
    index = int(request.GET.get('index'))
    print(index)
    context = {
        "data":DB[index:],
        "index":len(DB)
    }
    return JsonResponse(context)  #返回Json格式数据,序列化

def lang(request):
    uid = request.GET.get('uid')
    print("主页uid为:",uid)
    USER_QUEUE[uid] = queue.Queue()
    return render(request,"lang.html",{"uid":uid})

def lang_send(request):
    text = request.GET.get('text')
    print("接收到了:",text)
    for uid,q in USER_QUEUE.items():
        q.put(text)
    return HttpResponse("OK")

def lang_get(request):
    uid = request.GET.get('uid')
    q = USER_QUEUE[uid]  # 获取本用户对应的队列
    result = {'status':True,'data':None}
    try:
        data = q.get(timeout=10)
        result['data'] = data
    except queue.Empty as e:
        result['status'] = False

    return JsonResponse(result)

下文将介绍另一种协议--WebSocket原理及其使用案例。

感谢您的三连!!!


网站公告

今日签到

点亮在社区的每一天
去签到