当前位置: 首页 > news >正文

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 实现代码发布系统项目

相关文章:

Django使用WebSocket

1、websocket 相关 实现一个系统&#xff0c;20 个用户同时打开网站&#xff0c;呈现出来一个群聊界面 解决方案 轮询&#xff1a;让浏览器每隔2s向后台发送一次请求&#xff0c;缺点&#xff1a;延迟&#xff0c;请求太多网站压力大 长轮询&#xff1a;客户端向服务端发送请…...

看完这篇 教你玩转渗透测试靶机Vulnhub——HarryPotter:Nagini

Vulnhub靶机HarryPotter:Nagini渗透测试详解 Vulnhub靶机介绍&#xff1a;Vulnhub靶机下载&#xff1a;Vulnhub靶机安装&#xff1a;Vulnhub靶机漏洞详解&#xff1a;①&#xff1a;信息收集&#xff1a;②&#xff1a;漏洞发现&#xff1a;③&#xff1a;SSRF漏洞利用&#xf…...

IPO要收紧?业内人士未予以完全确认

“IPO全面收紧、吃穿住等行业标的基本劝退&#xff08;除非行业龙头&#xff09;、科创板第五套标准暂停受理……”在上周末&#xff0c;一篇关于IPO收紧的“小作文”在投行圈内疯狂转发。 距离全面注册制正式实施已过去了5个半月&#xff0c;IPO节奏是否在发生较大变化&#…...

stable difussion Pytorch实现与测试

引言: Stable Diffusion是目前最火的AI绘画工具之一,它是一个免费开源的项目,可以被任何人免费部署和使用。通过Stable Diffusion,可以很轻松的通过文字描述,生成对应的图片。由于它是一个开源项目,开源社区(如:GitHub)中有很多插件和训练好的模型,我们可以直接使用。…...

Redis简述

Redis是什么Redis数据类型Redis应用场景缓存计数器分布式会话排行榜最新列表分布式锁消息队列 Redis出现的问题穿透击穿雪崩 Redis为什么速度快 Redis是什么 redis是一种高速缓存数据库 Redis数据类型 string hash list set zset Redis应用场景 缓存 Redis作为缓存层&…...

Redis 操作List

【分布式】Redis 分布式之List_redissonclient.getlist_比嗨皮兔的博客-CSDN博客 说明 配置文件参考&#xff1a;https://blog.csdn.net/qq_38428623/article/details/123217001?utm_sourceapp&app_version5.1.1&codeapp_1562916241&uLinkIdusr1mkqgl919blen ——…...

多个List 合并变成一个List+一个List 根据某个字段相等的另一个字段相加,并排序变成新的List

List<CurveTimeAndValueDomain> curves new ArrayList<>();for (int i 0; i < columnNames.size(); i){if (columnNames.get(i).equals(PlantConstant.TENDOWNFX) || columnNames.get(i).equals(PlantConstant.TENDOWNQP)) {//10千伏以下 数据 进行缓慢处理cu…...

华为流程体系:流程架构「OES方法」

目录 内容简介 OES方法 端到端的流程 专栏列表 CSDN学院 作者简介 内容简介 今天继续来谈谈华为流程体系中的流程架构。 在前期的内容已经介绍过 POS 流程架构的方法。 这里就先回顾一下 POS 方法的相关内容&#xff1a; 关于 POS&#xff0c;大家可以参看上面的这张图…...

c# 创建一个未定义类的临时对象列表

使用场景&#xff1a;要使用的数据太多&#xff0c;列表/字典无法满足需求&#xff0c;需要传入对象&#xff0c;但是又不想创建模型 new[] 是一种用于创建匿名类型数组的写法。它是 C# 中的一种语法糖&#xff0c;用于简化数组的初始化过程。 在下面代码示例中&#xff0c;ne…...

el-button增加下载功能

vue3和element-plus <el-uploadv-model:file-list"fileList"action"/api/upload"multiple:limit"1":headers"headers" ><el-button type"primary">选择文件</el-button><template #file"{ file …...

prometheus和cAdvisor组合

文章目录 docker内部署PromethuesPrometheuscAdvisorPrometheus和cAdvisor关系配置 docker内部署Promethues Prometheus Prometheus是一个开源的系统监控和报警工具&#xff0c;由SoundCloud开发并在2012年捐赠给了Cloud Native Computing Foundation (CNCF)。它被广泛用于监…...

计算机网络(2) --- 网络套接字UDP

计算机网络&#xff08;1&#xff09; --- 网络介绍_哈里沃克的博客-CSDN博客https://blog.csdn.net/m0_63488627/article/details/131967378?spm1001.2014.3001.5501 目录 1.端口号 2.TCP与UDP协议 1.TCP协议介绍 1.TCP协议 2.UDP协议 3.理解 2.网络字节序 发送逻辑…...

Idea 结合docker-compose 发布项目

Idea 结合docker-compose 发布项目 这里写目录标题 Idea 结合docker-compose 发布项目Docker 开启远程访问功能 添加相应端口配置IDEA 链接Docker配置项目 docker-compose.yml本地还需要安装 dockerwin11 安装本地Docker 可能存在问题 Linux内核不是最新 Docker 开启远程访问功…...

django

django学习 初识Django1.安装django2.创建项目2.1 在终端2.2 Pycharm 3. 创建app4.快速上手4.1 再写一个页面4.2 templates模板4.3 静态文件4.3.1 static目录4.3.2 引用静态文件 5.模板语法案例&#xff1a;伪联通新闻中心6.请求和响应案例&#xff1a;用户登录7.数据库操作7.1…...

c++游戏框架

游戏类 class Sprite { public:Sprite(int x, int y, int w, int h, const char* imagePath);~Sprite();void render(SDL_Renderer* renderer);void move(int x, int y); private:SDL_Texture* texture_;SDL_Rect rect_; }; 物理引擎类 class PhysicsEngine { public:Physi…...

v-model绑定checkbox无法动态更新视图

在vue2中使用v-model绑定checkbox <input type"checkbox" v-model"isChecked" :valueisChecked change"handleCheckboxChange" />监听change事件&#xff0c;并在change事件中做一些特殊处理&#xff0c;比如用户在登录时有没有阅读过隐私…...

原生html—摆脱ps、excel 在线绘制财务表格加水印(html绘制表格js加水印)

文章目录 ⭐前言⭐html标签&#x1f496;table表格的属性&#x1f496;实现财务报表 ⭐结束 ⭐前言 大家好&#xff0c;我是yma16&#xff0c;本文分享原生html——绘制表格报表加水印。 背景&#xff1a;解决没有ps的情况下使用前端html制作表格报表。 html介绍 HTML&#xf…...

微信小程序配置上传多个u-upload上传

微信小程序配置上传多个u-upload上传 使用的是uView框架 微信小程序配置上传多个u-upload上传图片 场景需求&#xff1a;根据PC端配置项追加图片配置 小程序根据配置的图片数量&#xff0c;图片名称&#xff0c;进行上传图片 难度在于 我们不知道用户会追加多少个图片配置字段 …...

python使用win32com库实现对Excel的操作

使用win32com库实现对Excel的操作 1. 引言 在日常工作中&#xff0c;我们经常需要对Excel文件进行操作&#xff0c;例如读取和写入数据、格式化和样式、插入和删除等。而使用Python的win32com库&#xff0c;我们可以通过代码来实现对Excel的自动化操作&#xff0c;提高工作效…...

<Maven>项目依赖导入Maven本地仓库命令

项目工程pom.xml文件打开&#xff1a;查看报错的依赖, 将jar包放在D盘(或者其它路径都可)根目录下,在windows黑窗口执行以下命令; 举例&#xff1a;jar包名称&#xff1a; 1.api-1.0-SNAPSHOT102.jar 2.coms-cache-1.0-SNAPSHOT.jar 命令&#xff1a; mvn install:install-fi…...

爬虫006_python中的运算符_算术运算符_赋值运算符_复合赋值运算符_比较运算符_逻辑运算符_逻辑运算符性能提升---python工作笔记024

首先看加减乘除 然后看这里的 // 是取整数部分,不是四舍五入 然后%这个是取余数 然后**是,几次方那种 指数...

CPU Architecture Methodologies

MMU MMU(Memory Management Unit) 负责将逻辑地址转化为物理地址对于现代处理器来说&#xff0c;一般每个core都有自己的 MMU页表等数据结构保存在 TLB NUMA Non-uniform memory access (NUMA) is a computer memory design used in multiprocessing, where the memory access…...

Spring的@Scheduled

Spring的Scheduled的默认线程池数量为1&#xff0c;也就是说定时任务是单线程执行的。这意味着最多同时只有一个任务在执行。当一个任务还在执行时&#xff0c;其他任务会等待其完成&#xff0c;然后按照其预定的执行策略依次执行。 测试代码&#xff1a; 启动类上加注解Enab…...

IP隧道技术原理

简介 IP隧道技术是一种将一个协议的数据包封装在另一个协议的数据包中进行传输的技术。在网络通信中&#xff0c;不同的网络协议之间可能存在不兼容的情况&#xff0c;这时候就需要使用IP隧道技术来解决这个问题。 原理 IP隧道技术的原理是将一个协议的数据包封装在另一个协议…...

Docker私有仓库

Docker私有仓库 Docker官方的Docker hub&#xff08;https://hub.docker.com&#xff09;是一个用于管理公共镜像的仓库&#xff0c;我们可以从上面拉取镜像到本地&#xff0c;也可以把我们自己的镜像推送上去。但是&#xff0c;有时候我们的服务器无法访问互联网&#xff0c;…...

LLM微调 | Prefix-Tuning, Prompt-Tuning, P-tuning, P-tuning-v2

🔥 下面我只是分析讲解下这些方法的原理以及具体代码是怎么实现的,不对效果进行评价,毕竟不同任务不同数据集效果差别还是挺大的。 文章目录 0、hard prompt & soft prompt区别1、Prefix-Tuning2、Prompt-Tuning3、P-tuning4、P-tuning-v25、来看看adapter,lora,pref…...

Ansible 的脚本 --- playbook 剧本

目录 playbook 剧本 playbooks 本身由以下各部分组成 定义、引用变量 指定远程主机sudo切换用户 when条件判断 迭代 Templates 模块 1.先准备一个以 .j2 为后缀的 template 模板文件&#xff0c;设置引用的变量 2.修改主机清单文件&#xff0c;使用主机变量定义一个变…...

CSP-J模拟赛 / 买文具

限制条件 时间限制: 1000 ms, 空间限制: 256 MB 输入文件: pen.in, 输出文件&#xff1a;pen.out 题目描述 开学啦&#xff0c;为了准备新学期的课程学习&#xff0c;小贝到商店买文具。小贝买完文具回家&#xff0c;告诉妈妈说她买了钢笔、圆珠笔和铅笔总共x支&#xff0c…...

leecode算法--每日一题1

二分查找 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在返回下标&#xff0c;否则返回 -1。 前提条件必须满足&#xff1a; 目标数组必须是有序数组 所以…...

LViT:语言与视觉Transformer在医学图像分割

论文链接&#xff1a;https://arxiv.org/abs/2206.14718 代码链接&#xff1a;GitHub - HUANGLIZI/LViT: This repo is the official implementation of "LViT: Language meets Vision Transformer in Medical Image Segmentation" (IEEE Transactions on Medical I…...