Django使用WebSocket

1、websocket 相关

实现一个系统,20 个用户同时打开网站,呈现出来一个群聊界面

解决方案

  • 轮询:让浏览器每隔2s向后台发送一次请求,缺点:延迟,请求太多网站压力大

  • 轮询:客户端向服务端发送请求,服务器最多宕20s,一旦有数据接入,就立即返回。数据的响应没有延迟时间。

  • websocket:客户端和服务端创建连接后,不断开,实现双向通信

在这里插入图片描述

轮询

  • 访问 /home/ 显示的聊天室界面

  • 点击发送内容,数据可以发送到后台

  • 定时获取消息,发送到前端

长轮询

在这里插入图片描述

  • 访问/home/ 显示聊天界面, → 每个用户创建一个队列

  • 点击发送内容,数据也可以发送到后台 → 扔到每个用户的队列中

  • 递归获取消息, 去自己的队列中获取数据,然后展示在界面中。

问题:

  • 服务端持有连接,压力是否会很大?

    如果基于IO多复用 + 异步,还会有这种情况吗? 可以

  • 如果后台有100线程,同时对应100个用户的请求,则在等待期间(假设10s),这100个用户则一直占用县城,如果有更多的用户请求,则需等待释放。

webSocket

原来的web中:

  • http协议: 无状态 & 短连接

    • 客户端主动连接服务端。

    • 客户端向服务端发送消息,服务端接收后,返回数据

    • 客户端接收到数据

    • 断开连接

  • https协议 = http协议 + 对数据进行加密

我们在开发过程中,想要保留一些状态信息,基于Cookie来做

现在支持:

  • http协议:一次请求一次响应

  • websocket协议: 创建持有的连接不断开,基于这个连接可以进行收发数据。【服务端向客户端主动推送消息】

    • web聊天室

    • 实时图标,柱状图、饼图(echarts)

WebSocket原理

  • http协议

    • 连接

    • 数据传输

    • 断开连接

  • websocket 协议 → 建立在 http 协议之上

    • 连接, 客户端发出请求

    • 握手(验证), 客户端发送一段消息,后端接收到消息后,做一些特殊处理并返回。服务端支持 websocket 协议

      https://www.cnblogs.com/wupeiqi/p/6558766.html

  • 客户端向服务端发送握手信息

GET /chatsocket HTTP/1.1
Host: 127.0.0.1:8002
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: http://localhost:63342
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: mnwFxiOlctXFN/DeMt1Amg==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
...
...
  • 服务端接收
    • 接收后的加密过程
// Sec-WebSocket-Key 与 magic String 进行拼接
Sec-WebSocket-Key: mnwFxiOlctXFN/DeMt1Amg==  
magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
v1 = "mnwFxiOlctXFN/DeMt1Amg==" +  '258EAFA5-E914-47DA-95CA-C5AB0DC85B11'v2 = hmac1(v1)  // 通过 hmac1 进行加密
v3 = base64(v2) // 通过 base64 加密
  • 返回数据
HTTP/1.1 101 Switching Protocols
Upgrade:websocket
Connection: Upgrade
Sec-WebSocket-Accept: 密文
  • 收发数据(加密)

    • 先获取第 2 个字节,对应 8 位

    • 在获取第二个字节的后 7 位

  • 断开连接

Django 框架实现 WebSocket

Django 默认不支持WebSocket,安装第三方组件

pip install channels

版本不能超过4.0,最好是3.0.5,不然不能成功启动asgi

配置:

django channels - 武沛齐 - 博客园

  • 注册 channels
	INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','channels','app01.apps.App01Config']
  • 在 settings.py 中添加 asgi_application
	ASGI_APPLICATION = 'ws_demo.asgi.application'
  • 修改 asgi.py文件
	import osfrom django.core.asgi import get_asgi_applicationfrom channels.routing import ProtocolTypeRouter, URLRouterfrom ws_demo import routingsos.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ws_demo.settings')# application = get_asgi_application()# 支持 http 和 WebSocket 请求application = ProtocolTypeRouter({"http": get_asgi_application(),  # 自动找 urls.py , 找视图函数  --》 http"websocket": URLRouter(routings.websocket_urlpatterns),  # routing(urls)、 consumers(views)})
  • 在 settings.py同级目录下,创建routings.py
from django.urls import re_pathfrom app01 import consumerswebsocket_urlpatterns = [# 示例 url : xxxxx/room/x1/re_path(r"room/(?P<group>\w+)/$", consumers.ChatConsumer.as_asgi())
]
  • 在 app01 目录下,创建consumers.py ,用于设置 WebSocket 请求
from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumerclass ChatConsumer(WebsocketConsumer):  # 继承WebsocketConsumerdef websocket_connect(self, message):print("有人进行连接了。。。。")# 有客户端向后端发送 WebSocket 连接的请求时,自动触发(握手)self.accept()def websocket_receive(self, message):# 浏览器基于 WebSocket 向后端发送数据,自动触发接收消息print(message)self.send("不要回复不要回复!!!")def websocket_disconnect(self, message):# 客户端向服务端断开连接时,自动触发print("连接断开!!")raise StopConsumer()

django 中,需要了解:

  • wsg:

在这里插入图片描述

  • asgi: wsgi + 异步 + WebSocket

在这里插入图片描述

聊天室

  • 访问地址看到聊天室界面,使用 http 请求

  • 让客户端主动向服务端发起 websocket连接,服务端接收到连接后,进行握手

    • 客户端向后台发布WebSocket请求

      var socket = new WebSocket("ws://localhost:8000/room/123/")
      
    • 服务端接收消息

         from channels.generic.websocket import WebsocketConsumerfrom channels.exceptions import StopConsumerclass ChatConsumer(WebsocketConsumer):  # 继承WebsocketConsumerdef websocket_connect(self, message):print("有人进行连接了。。。。")# 有客户端向后端发送 WebSocket 连接的请求时,自动触发(握手)self.accept()
      
  • 收发消息(客户端向服务端发消息)

    • 客户端发送消息
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><style>.message {height: 300px;border: 1px solid #ddd;width: 100%;}</style>
</head>
<body>
<div class="message" id="message"></div>
<div><label><input type="text" placeholder="请输入" id="txt"></label><input type="button" value="发送" οnclick="sendMessage()">
</div>
<script>// 创建websocket对象,向后台发送请求let socket = new WebSocket("ws://localhost:8000/room/123/");function sendMessage(){let tag = document.getElementById("txt");socket.send(tag.value);}</script>
</body>
</html>
  • 服务端接收消息
def websocket_receive(self, message):# 浏览器基于 WebSocket 向后端发送数据,自动触发接收消息text = message["text"]print("接收到的消息为:", text)
  • 收发消息(服务端主动发给客户端)
# 连接之后,服务端给客户端发送消息
self.send("来了啊 !!!")
// 回调函数,客户端接收服务端消息
socket.onmessage = function (event){console.log(event.data)
}

其他方法

// 创建websocket对象,向后台发送请求
let socket = new WebSocket("ws://localhost:8000/room/123/");// 当客户端和服务端刚创建好连接(self.accept)之后,自动触发.
socket.onopen = function (event){let tag = document.createElement("div");tag.innerText = "[连接成功]";document.getElementById("message").appendChild(tag);
}// 回调函数,客户端接收服务端消息
socket.onmessage = function (event){let tag = document.createElement("div");tag.innerText = event.data;document.getElementById("message").appendChild(tag);
}// 当断开连接时,触发该函数
socket.onclose =function (event){let tag = document.createElement("div");tag.innerText = "[连接断开]";document.getElementById("message").appendChild(tag);
}function sendMessage(){let tag = document.getElementById("txt");socket.send(tag.value);
}function closeMessage(){socket.close();
}function handleKeyPress(event){if (event.keyCode === 13){document.getElementById("send").click();document.getElementById("txt").value = "";}
}document.onkeydown = handleKeyPress;
    def websocket_connect(self, message):print("有人进行连接了。。。。")# 有客户端向后端发送 WebSocket 连接的请求时,自动触发(握手)self.accept()# 连接之后,服务端给客户端发送消息self.send("来了啊 !!!")def websocket_receive(self, message):# 浏览器基于 WebSocket 向后端发送数据,自动触发接收消息text = message["text"]print("接收到的消息为:", text)# 当接收的值为【关闭】,则服务端关闭连接if text == "close":self.close()returnelse:self.send(text + "NB")def websocket_disconnect(self, message):# 客户端向服务端断开连接时,自动触发print("断开连接!!!")# 当客户端断开连接时,服务端也需关闭与客户端的连接,连接是双向的raise StopConsumer()

小结

现在的交互还是停留在对某个人的操作

群聊

基于 channels 中提供的channel layers 来实现

  • settings 中配置
# 声明基于内存的 channel layers
CHANNEL_LAYERS = {"default": {"BACKEND": "channels.layers.InMemoryChannelLayer"}
}

也可声明基于 redis 的 channel layer → pip install channels-redis

# 基于redis 内存的 channel layers
CHANNEL_LAYERS = {"default": {"BACKEND": "channels_redis.core.RedisChannelLayer","CONFIG": {"hosts": ["redis://10.211.55.25:6379/1"]}}
}
  • consumers 中特殊的代码
from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer
from asgiref.sync import async_to_syncclass ChatConsumer(WebsocketConsumer):  # 继承WebsocketConsumerdef websocket_connect(self, message):# 接收客户端的连接self.accept()print("连接成功!!!")# 获取群号group_num = self.scope["url_route"]["kwargs"].get("group")# 将这个客户端的链接对象添加到某个地方(内存或者 redis)# self.channel_layer.group_add(group_num, self.channel_name)async_to_sync(self.channel_layer.group_add)(group_num, self.channel_name)def websocket_receive(self, message):# 浏览器基于 WebSocket 向后端发送数据,自动触发接收消息text = message["text"]print("接收到的消息为:", text)group_num = self.scope["url_route"]["kwargs"].get("group")print("group_num", group_num)# 通知组内的所有的客户端,执行 xx_oo方法,在方法中可以自定义任意的功能# self.channel_layer.group_send(group_num, {"type": "xx.oo", "message": message})async_to_sync(self.channel_layer.group_send)(group_num, {"type": "xx.oo", "message": message})def xx_oo(self, event):text = event["message"]["text"]print("发送的 text:", text)self.send(text)  # 给组中的每一个人去发送消息def websocket_disconnect(self, message):# 客户端向服务端断开连接时,自动触发print("断开连接!!!")group_num = self.scope["url_route"]["kwargs"].get("group_num")# self.channel_layer.group_discard(group_num, self.channel_name)async_to_sync(self.channel_layer.group_discard)(group_num, self.channel_name)raise StopConsumer()
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><style>.message {height: 300px;border: 1px solid #ddd;width: 100%;}</style>
</head>
<body>
<div class="message" id="message"></div>
<div><label><input type="text" placeholder="请输入" id="txt"></label><input type="button" value="发送" οnclick="sendMessage()" id="send"><input type="button" value="关闭链接" οnclick="closeMessage()">
</div>
<script>// 创建websocket对象,向后台发送请求let socket = new WebSocket("ws://localhost:8000/room/{{ group_num }}/");// 当客户端和服务端刚创建好连接(self.accept)之后,自动触发.socket.onopen = function (event){let tag = document.createElement("div");tag.innerText = "[连接成功]";document.getElementById("message").appendChild(tag);}// 回调函数,客户端接收服务端消息socket.onmessage = function (event){let tag = document.createElement("div");tag.innerText = event.data;document.getElementById("message").appendChild(tag);}// 当断开连接时,触发该函数socket.onclose =function (event){let tag = document.createElement("div");tag.innerText = "[连接断开]";document.getElementById("message").appendChild(tag);}function sendMessage(){let tag = document.getElementById("txt");socket.send(tag.value);}function closeMessage(){socket.close();}function handleKeyPress(event){if (event.keyCode === 13){document.getElementById("send").click();document.getElementById("txt").value = "";}}document.onkeydown = handleKeyPress;</script>
</body>
</html>

总结

  • WebSocket 是什么? 协议

  • django 中实现 WebSocket, channels 组件

    • 单独连接和收发数据

    • 手动创建列表 & channel layers

运维&运维开发的同学,使用 WebSocket 实现代码发布系统项目

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/16560.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue2 第九节 过滤器

&#xff08;1&#xff09;定义&#xff1a;对要显示的数据进行特定格式化后再显示 &#xff08;2&#xff09;语法&#xff1a; ① 注册过滤器 1&#xff09;Vue.filter(name, callback) 全局过滤器 2&#xff09; new Vue({filters:{}}) 局部过滤器 ② 使用过滤器 1&…

14. Spring AOP 的组成和实现

目录 1. Spring AOP 简介 2. AOP 的组成 2.1 切面&#xff08;Aspect&#xff09; 2.2 连接点&#xff08;Join Point&#xff09; 2.3 切点&#xff08;Pointcut&#xff09; 2.4 通知&#xff08;Advice&#xff09; 3. Spring AOP的实现 3.1 新建项目 3.2 添加 AOP …

Stable Doodle:Stability AI推出的一款零门槛AI绘画神器

Stable Doodle是由Stability AI推出的一款零门槛AI绘画神器&#xff0c;可以将简单的草图转化为精美的图像。它可以将随手的塗鴉草稿转化为高畫質的完成圖&#xff0c;让用户能够以更快的速度将想法转化为精美的艺术作品。Stable Doodle利用最新的Stable Diffusion模型&#xf…

C#实现读写CSV文件的方法详解

目录 CSV文件标准 文件示例RFC 4180简化标准读写CSV文件 使用CsvHelper使用自定义方法总结 项目中经常遇到CSV文件的读写需求&#xff0c;其中的难点主要是CSV文件的解析。本文会介绍CsvHelper、TextFieldParser、正则表达式三种解析CSV文件的方法&#xff0c;顺带也会介绍一…

框架的知识点整理

目录 1、什么是Spring框架&#xff1f;Spring框架有哪些主要模块&#xff1f; 2 、 使用Spring框架有什么好处&#xff1f; 3、Spring MVC 工作原理 1、什么是Spring框架&#xff1f;Spring框架有哪些主要模块&#xff1f; Spring框架是一个开源的轻量级的Java应用程序开…

x86架构ubuntu22下运行WILL模拟器dophin

0. 环境 i5实体机ubuntu22 1. 安装依赖 $ sudo apt install build-essential git cmake ffmpeg libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libevdev-dev libusb-1.0-0-dev libxrandr-dev libxi-dev libpangocairo-1.0-0 qt6-base-private-dev libblueto…

腾讯云—自动挂载云盘

腾讯云&#xff0c;稍微麻烦了点。 腾讯云服务器&#xff0c;镜像为opencloudos 8。 ### 1、挂载云盘bash #首先通过以下命令&#xff0c;能够看到新的数据盘&#xff0c;如果不能需要通过腾讯云控制台卸载后&#xff0c;重新挂载&#xff0c;并重启服务器。 fdisk -l#为 /dev…

BES2700 SDK绝对时间获取方法

1 代码 2 实验 log 需要换算下

Tensorflow benchmark 实操指南

环境搭建篇见环境搭建-CentOS7下Nvidia Docker容器基于TensorFlow1.15测试GPU_东方狱兔的博客-CSDN博客 1. 下载Benchmarks源码 从 TensorFlow 的 Github 仓库上下载 TensorFlow Benchmarks&#xff0c;可以通过以下命令来下载 https://github.com/tensorflow/benchmarks 我…

【Unity2D】粒子特效

为什么要使用粒子特效 Unity 的粒子系统是一种非常强大的工具&#xff0c;可以用来创建各种各样的游戏特效&#xff0c;如火焰、烟雾、水流、爆炸等等。使用粒子特效可以使一些游戏动画更加真实或者使游戏效果更加丰富。 粒子特效的使用 在Hierarchy界面右键添加Effects->…

软件测试工程师的职业规划,你都做好了吗?

第一阶段&#xff1a;初级测试工程师 一般刚刚入门&#xff0c;熟悉基本的测试流程&#xff0c;此时已打基础为主。入门薪资一般在6000-8000元之间。 具体工作是&#xff1a; &#xff08;1&#xff09; 按照测试方案和流程对产品进行功能测试&#xff0c;检查产品是否有缺陷…

C#文件操作从入门到精通(2)——查看某个dll中有哪些函数

kernel32.dll中含有ini文件操作使用的函数,我们可以通过VisualStudio自带的dumpbin.exe查看dll所包含的函数,操作步骤如下: 1、找到dumpbin.exe所在的文件夹 我的电脑中安装了VisualStudio2019社区版以及VisualStudio2017Professional,但是我发现VisualStudio2019社区版中…

spring项目中idea提示Application context not configured for this file

今天在重构项目的时候&#xff0c;碰到一个问题。就是在spring底下&#xff0c;有一个包里面的所有配置类&#xff0c;在idea的开发工具类底下提示&#xff0c;Application context not configured for this file&#xff0c;如图所示 一开始以为是警告&#xff0c;不予处理&am…

MySQL中的函数

系列文章目录 MySQL常见的几种约束 文章目录 系列文章目录前言一、单行函数1.字符串函数 &#xff08;String StringBuilder&#xff09;2.数值函数 &#xff08;Math&#xff09;3.日期与时间函数4.流程函数&#xff08; IF SWITCH&#xff09;5.JSON函数6.其他函数 二、多行…

TreeMap的底层实现

0. 你需要知道的TreeMap的内置属性 0.1 节点属性 K key; // 键 V value; // 值 Entry<K,V> left; // 左子节点 Entry<K,V> right; // 右子节点 Entry<K,V> parent; // 父节点 boolean color; // 节点的颜色0.2 成员变量 //比较器对象private f…

rsync下行同步+inotify实时同步部署

目录 一、rsync简介 1.2 同步方式 1.2.1 全量备份 1.2.2 增量备份 1.2.3 差量备份 1.3 rsync的特点 1.4 rsync的优势与不足 1.5 rsync与cp、scp对比 1.6 rsync同类服务 二、rsync源服务器的关系 三、配置rsync源 3.1 基本思路 3.2 配置文件rsyncd.conf 3.3 独立…

HDFS Erasure coding-纠删码介绍和原理

HDFS Erasure coding-纠删码介绍和原理 三副本策略弊端Erasure Coding&#xff08;EC&#xff09;简介Reed- Solomon&#xff08;RS&#xff09;码 EC架构 三副本策略弊端 为了提供容错能力&#xff0c;hdfs回根据replication factor&#xff08;复制因子&#xff09;在不同的…

idea application.yml配置文件没有提示或读不到配置

1.首先确定你的resources文件夹正常且yml文件图表和下面一样 不一样的右键去设置 2.确保你已经缩进了且层级关系正常 3.如果以上都不是&#xff0c;先考虑删除.idea重开试试 4.以上解决不了就装以下两个插件解决

目标检测之3维合成

现在有一系列的图片&#xff0c;图片之间可以按照z轴方向进行排列。图片经过了目标检测&#xff0c;输出了一系列的检测框&#xff0c;现在的需求是将检测框按类别进行合成&#xff0c;以在3维上生成检测结果。 思路&#xff1a;将图片按照z轴方向排列&#xff0c;以z轴索引作…

微分流形2:流形上的矢量场和张量场

来了来了&#xff0c;切向量&#xff0c;切空间。流形上的所有的线性泛函的集合&#xff0c;注意是函数的集合。然后取流形上的某点p&#xff0c;它的切向量为&#xff0c;线性泛函到实数的映射。没错&#xff0c;是函数到实数的映射&#xff0c;是不是想到了求导。我们要逐渐熟…