Django Channels、WS协议及同步与异步详解

这篇具有很好参考价值的文章主要介绍了Django Channels、WS协议及同步与异步详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 同步与异步

在 Django 中,同步和异步主要涉及到请求处理的方式。这两种方式的主要区别在于它们如何处理多个并发请求:

  • 同步(Synchronous):在同步模式下,Django 会为每个请求创建一个单独的线程或进程。这意味着,如果一个请求正在等待响应(例如,等待数据库查询返回结果),那么整个线程或进程将被阻塞,直到响应返回。这可能会导致资源的浪费,因为在等待期间,线程或进程不能做其他任何事情。
  • 异步(Asynchronous):与同步模式不同,异步模式允许单个线程或进程同时处理多个请求。当一个请求需要等待响应时(例如,等待数据库查询返回结果),线程或进程可以切换到另一个请求,继续执行其他任务,而不是被阻塞。这样可以更有效地利用系统资源,提高并发处理能力。

Django 3.1 版本开始引入了对异步视图和中间件的支持,这意味着你可以编写异步的视图函数,这些函数可以使用 Python 的 asyncawait 关键字进行定义。这使得 Django 可以更好地处理 I/O 密集型任务,如 HTTP 请求、数据库操作和文件读写等。

然而需要注意的是,并非所有的 Django 组件都支持异步操作。例如,Django 的 ORM(对象关系映射)目前仍然是同步的,这意味着你不能在异步视图或中间件中直接使用它。如果你需要在异步代码中执行数据库操作,你需要使用 Django 提供的 sync_to_asyncasync_to_sync 函数来确保数据库操作在同步环境中执行。

总的来说,同步和异步各有优势和适用场景。对于 CPU 密集型任务,同步模式可能更合适;而对于 I/O 密集型任务,异步模式可能会带来更好的性能。在实际开发中,你可能需要根据应用的具体需求和性能要求来选择使用同步还是异步。

(1)同步代码样例

现在我们来看一些同步与异步的样例,首先是同步视图,在下面这个例子中,当请求到达 sync_view 时,Django 将等待视图函数完成后才会处理下一个请求:

from django.http import HttpResponse
from django.shortcuts import render

def sync_view(request):  # 这是一个同步视图
    return HttpResponse('Hello, this is a synchronous view!')

下面这个例子在视图中同步地从数据库获取所有的博客对象,然后将它们传递给模板:

from django.shortcuts import render
from .models import Blog

def sync_view(request):
    blogs = Blog.objects.all()  # 获取所有的博客对象
    return render(request, 'blog/index.html', {'blogs': blogs})

(2)异步代码样例

在下面这个例子中,async_view 是一个异步视图。当请求到达这个视图时,Django 可以在等待 asyncio.sleep(1) 完成时处理其他请求:

import asyncio
from django.http import HttpResponse

async def async_view(request):  # 这是一个异步视图
    await asyncio.sleep(1)
    return HttpResponse('Hello, this is an asynchronous view!')

请注意,要使用异步视图,你需要确保你的 Django 项目正在运行在支持异步的 ASGI 服务器上,而不是传统的 WSGI 服务器。此外,你的中间件和任何你在视图中调用的代码也必须支持异步。否则,你可能会遇到问题。如果你的代码库主要是同步的,那么最好坚持使用同步视图。如果你正在编写新的、主要使用 Python 的异步库的代码,那么异步视图可能会很有用。请记住,混合使用同步和异步代码可以很复杂,需要谨慎对待。

再来看下面的例子,我们创建了一个新的 get_blogs 异步函数,它使用 run_in_executor 方法在一个单独的线程中运行数据库查询。这允许 Django 在等待数据库查询完成时处理其他请求:

import asyncio
from django.http import JsonResponse
from .models import Blog

async def async_view(request):  # 异步获取所有的博客对象
    blogs = await get_blogs()
    return JsonResponse({'blogs': list(blogs.values())})

async def get_blogs():
    loop = asyncio.get_event_loop()
    blogs = await loop.run_in_executor(None, Blog.objects.all)
    return blogs

请注意,虽然这个示例展示了如何在 Django 视图中使用异步代码操作数据库,但是 Django 的数据库层目前还不支持原生的异步操作。因此,在实践中,你可能需要使用像 asgiref.sync_to_async 这样的工具来安全地在异步视图中执行同步数据库操作。同时,你也需要确保你的数据库驱动程序和数据库服务器能够处理并发连接。否则,你可能会遇到性能问题或错误。如果你不确定如何正确地使用异步代码,那么最好使用同步视图和同步数据库操作。

2. WebSocket

WebSocket 是一种用于在 Web 和移动应用程序之间进行实时通信的新标准。WebSocket 设计为在 Web 浏览器和 Web 服务器之间实现,但也可以由客户端或服务器应用程序使用。WebSocket 是一种提供单个 TCP 连接上的全双工通信通道的协议,可以实现服务器和客户端之间的实时交互。

WebSocket 与 HTTP 不同,其主要区别如下:

  • 通信方式:HTTP 是单向的,客户端发送请求,服务器发送响应。而 WebSocket 是双向的,在客户端-服务器通信的场景中使用的全双工协议,即客户端和服务器可以同时发送和接收数据。
  • 连接:HTTP 每次请求都需要重新建立连接,而 WebSocket 使用长连接实现数据实时推送。一旦通信链接建立和连接打开后,消息交换将以双向模式进行,客户端-服务器之间的连接会持久存在。
  • 数据传输:HTTP 协议中的数据传输是文本格式的,而 WebSocket 可以传输文本和二进制数据。
  • 性能:由于 HTTP 的每次请求都需要建立连接和断开连接,而 WebSocket 可以在一次连接上进行多次通信,因此 WebSocket 在性能上比 HTTP 有优势。
  • 应用场景:HTTP 主要用于客户端和服务器之间的请求和响应,如浏览器请求网页和服务器返回网页的 HTML 文件。WebSocket 可以实现双向通信,常常用于实时通信场景。
  • 协议头:HTTP 协议头的大小从200字节到2KB不等,常见大小是700-800字节。而 WebSocket 协议头相对较小,这使得其在高频率、小数据量的通信场景下更有优势。
  • 状态:HTTP 是无状态协议,而 WebSocket 是有状态协议。这意味着客户端和服务器之间的连接将保持活动状态,直到被任何一方(客户端或服务器)终止。

两种协议都位于 OSI 模型的第七层,并依赖于第四层的 TCP。尽管它们是不同的,但 RFC 6455 指出,WebSocket 旨在通过 HTTP 端口443和80工作,并支持 HTTP 代理和中介,从而使其与 HTTP 兼容。为了实现兼容性,WebSocket 握手使用 HTTP Upgrade 头从 HTTP 协议切换到 WebSocket 协议。

WebSocket 协议使得 Web 浏览器(或其他客户端应用程序)和 Web 服务器之间可以在不需要客户端请求的情况下发送内容,以及在保持连接打开的同时传递消息。这样,客户端和服务器之间可以进行双向持续的对话。通信通常是通过 TCP 端口号443(或80,如果是非安全连接)进行的,这对于使用防火墙阻止非 Web Internet 连接的环境是有利的。

在 Django 中实现 WebSocket,你可以选择使用 channels 或者 dwebsocket。但是,channels 被更广泛地使用,因为它可以完美地集成到 Django 的生态系统中。

3. 在JavaScript中使用WebSocket

(1)创建 WebSocket 对象

let ws = new WebSocket('ws://localhost:8888');  // 如果是安全连接则地址为wss://...

(2)连接成功时的回调函数

当 WebSocket 连接成功时,onopen 事件会被触发。你可以在这个函数中发送消息到服务器:

ws.onopen = function(params) {
    console.log('客户端连接成功');
    ws.send('hello');  // 向服务器发送消息
};

(3)从服务器接收信息时的回调函数

当从服务器接收到信息时,onmessage 事件会被触发。你可以在这个函数中处理接收到的数据:

ws.onmessage = function(e) {
    console.log('收到服务器响应', e.data);
};

(4)连接关闭时的回调函数

当连接关闭后,onclose 事件会被触发。你可以在这个函数中处理连接关闭后的逻辑:

ws.onclose = function(e) {
    console.log("关闭客户端连接");
};

(5)连接失败时的回调函数

ws.onerror = function(e) {
    console.log("连接失败");
};

(6)监听窗口关闭事件

当窗口关闭时,主动去关闭 WebSocket 连接,防止连接还没断开就关闭窗口,这样服务端会抛异常:

window.onbeforeunload = function() {
    ws.close();
}

4. Django Channels

Django Channels 是一个开源框架,它扩展了 Django 的功能,使得 Django 不仅可以处理 HTTP,还可以处理需要长时间连接的协议,如 WebSocket、MQTT(消息队列遥测传输)、聊天协议、广播等实时应用。

Channels 允许 Django 项目支持“长连接”,它用 ASGI 替换了 Django 的默认 WSGI。ASGI(Asynchronous Server Gateway Interface)为异步 Python Web 服务器和应用程序提供了一个接口,同时支持 WSGI 提供的所有功能。

Channels 保留了 Django 的同步行为,并添加了一层异步协议,允许用户编写完全同步、异步或两者混合的视图(Views)。Django Channels 提供了一种通信系统,叫做 Channel Layer,它可以让多个 Consumer 实例之间互相通信,以及与外部 Django 程序实现互通。

  • Channel Layer 主要包括两种抽象概念:Channel 和 Group。Channel 是一个发送消息的通道,每个 Channel 都有一个名称,拥有这个名称的人都可以往 Channel 里面发送消息。Group 是多个 Channel 的集合,每个 Group 都有一个名称,拥有这个名称的人都可以往这个 Group 里添加/删除 Channel,也可以往 Group 里发送消息。Group 内的所有 Channel 都可以收到,但是不能给 Group 内的具体某个 Channel 发送消息。使用 Django Channels 可以实现一些实时通讯的功能,如在线聊天室、游戏、通知等。
  • Consumer 是 Channels 的基本单位,相当于 Django 的视图,它是一个事件驱动的类,可以处理不同类型的事件,如连接、断开、接收消息等,支持同步和异步应用程序。

现在我们来看一下 Django Channels 的样例,首先需要安装 Channels 和 Channels Redis:

pip install channels channels_redis

然后需要在你的项目设置中(settings.py 文件)配置 CHANNEL_LAYERS 需要添加 channels 到你的 INSTALLED_APPS 列表,并设置 ASGI_APPLICATIONCHANNEL_LAYERS。例如:

INSTALLED_APPS = [ 
    'channels',  # 添加此行
    'game.apps.GameConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

ASGI_APPLICATION = 'djangoapp.asgi.application'  # 'djangoapp'为你的项目名

CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {
            "hosts": [("127.0.0.1", 6379)],
        },
    },
}

在这个例子中,我们假设你正在本地运行一个 Redis 服务器,它监听在6379端口。如果你的 Redis 服务器在其他地方或者使用了不同的端口,你需要更新 hosts 设置。

接下来你需要在 Django App 的目录下创建一个路由文件 routing.py,作用相当于 HTTP 的 urls,并在其中定义你的 WebSocket 路由。我们先创建出来:

from django.urls import path

websocket_urlpatterns = [
]

此外,你还需要运行一个兼容的 ASGI 服务器,如 Daphne 或 Uvicorn。我们安装 Daphne:

pip install daphne

输入 daphne 命令查看是否可用,如果不可用说明应该是没有配置环境变量,按如下方式修改环境变量(需要重启系统):

sudo vim /etc/environment
在 PATH='xxx' 后面添加 ':/home/<用户名>/.local/bin'
即: 'xxx:/home/<用户名>/.local/bin'

为了在 Django 项目中使用 Daphne,你需要确保你的项目已经配置为使用 ASGI 而不是 WSGI。这通常意味着你需要在你的项目中创建一个 asgi.py 文件,并在你的设置文件中设置 ASGI_APPLICATION 变量(之前已经设置好了)。

现在我们配置 djangoapp/djangoapp/asgi.py 文件:

import os

from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
from game.routing import websocket_urlpatterns

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoapp.settings')  # 'djangoapp'为你的项目名

application = ProtocolTypeRouter({
    "http": get_asgi_application(),
    "websocket": AuthMiddlewareStack(URLRouter(websocket_urlpatterns))
})

现在我们在 Django App 目录下创建 consumers.py

from channels.generic.websocket import AsyncWebsocketConsumer
import json

class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.room_name = self.scope['url_route']['kwargs']['room_name']
        self.room_group_name = 'chat_%s' % self.room_name

        # 加入房间
        await self.channel_layer.group_add(
            self.room_group_name,
            self.channel_name
        )

        await self.accept()

    async def disconnect(self, close_code):
        # 离开房间
        await self.channel_layer.group_discard(
            self.room_group_name,
            self.channel_name
        )

    # 接收来自WebSocket的消息
    async def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json['message']

        # 发送消息到房间组
        await self.channel_layer.group_send(
            self.room_group_name,
            {
                'type': 'chat_message',
                'message': message
            }
        )

    # 接收来自房间组的消息
    async def chat_message(self, event):
        message = event['message']

        # 发送消息到WebSocket
        await self.send(text_data=json.dumps({
            'message': message
        }))

在这个例子中,我们创建了一个 ChatConsumer 类,它是一个异步的 WebSocket Consumer,这个类继承自 AsyncWebsocketConsumer,这是 Django Channels 提供的一个基础类。

其中的主要函数说明如下:

  • async def connect(self):当一个 WebSocket 连接打开时,这个方法会被调用。在这个方法中,我们从 URL 路由中获取房间名,并将其保存在 self.room_name 中。然后,我们创建一个房间组名,并将其保存在 self.room_group_name 中。然后,我们将当前的 Channel 添加到房间组中。最后,我们接受 WebSocket 连接。
  • async def disconnect(self, close_code):当一个 WebSocket 连接关闭时,这个方法会被调用。在这个方法中,我们将当前的 Channel 从房间组中移除。
  • async def receive(self, text_data):当从 WebSocket 接收到消息时,这个方法会被调用。在这个方法中,我们首先将接收到的文本数据解析为 JSON,然后我们从 JSON 数据中获取消息,并将其发送到房间组中。
  • async def chat_message(self, event):这是一个自定义的事件处理器方法,当从房间组接收到类型为 chat_message 的事件时,这个方法会被调用。在这个方法中,我们首先从事件中获取消息,然后我们将消息发送回 WebSocket。

总的来说,这段代码的功能为:当用户连接到 WebSocket 时,他们会被添加到一个名为 chat_{room_name} 的组中。当他们发送消息时,这个消息会被广播到他们所在的组中的所有其他用户。当他们接收到组中的消息时,这个消息会被发送回他们的 WebSocket,即实现了一个简单的聊天室功能。

最后我们定义一下 Consumer 的路由,以下代码将 URL 路径 ws/chat/{room_name}/ 映射到我们的 ChatConsumer。这意味着当用户连接到这个路径的 WebSocket 时,他们会被连接到聊天室:文章来源地址https://www.toymoban.com/news/detail-726636.html

from django.urls import re_path
from .consumers import ChatConsumer

websocket_urlpatterns = [
    re_path(r'ws/chat/(?P<room_name>\w+)/$', ChatConsumer.as_asgi()),
]

到了这里,关于Django Channels、WS协议及同步与异步详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • django channels实战(websocket底层原理和案例)

    asgi.py在django项目同名app目录下 django代码总结 报错如下: 1、不要安装最新的版本,建议安装指定版本 pip install -U channels==3.0.2 2、在python .manage.py runserver 0.0.0.0:8080启动的前提下,新开一个cmd窗口,启动daphne服务 1)首先,确保你已经安装了 Daphne。可以使用以下命令安装最新版

    2024年02月14日
    浏览(39)
  • Django利用Channels+websocket开发聊天室

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 前言 一、什么是Websocket? 2.Python-Django ASGI 3,Django开发聊天室或信息推送 前言 数据库系统课程设计要求,要开发一个B2B的售卖平台,本来开发浅薄的我,粗糙又基础的完成了一些基本的功能,想要

    2024年02月05日
    浏览(40)
  • Django框架:使用channels实现websocket,配置和项目实际使用

    依赖包: 项目目录结构: study_websocket         --study_websocket                 --__init__.py                 --settings.py                 --asgi.py                 --wsgi.py                 --urls.py         --chat                 --routing

    2024年02月15日
    浏览(38)
  • Python异步编程|ASGI 与 Django(附源码)

    异步服务网关接口(Asynchronous Server Gateway Interface,ASGI)秉承WSGI统一网关接口原则,在异步服务、框架和应用之间提供一个标准接口,同时兼容WSGI。 ASGI是根据统一接口的思想重新设计的新标准,你可能会有疑问,为什么不直接升级WSGI而去创造新的标准呢? WSGI是基于HTTP短连

    2024年02月14日
    浏览(29)
  • 【项目部署-apache】windows系统下apache部署django+channels

    1、原生的python3.8.5(默认安装,不更改安装目录),不要使用Anaconda做虚拟环境。 2、在windows系统下需要,mod_wsgi 模块。 创建虚拟环境:(在当前目录下创建名为 gzgs_alert 的虚拟环境) 激活gzgs_alert 虚拟环境,安装mod_wsgi: whl文件下载: 复制文件到虚拟环境的Scripts目录下: cmd进

    2024年04月26日
    浏览(35)
  • Django3框架-(2)-[使用websocket]:使用channels实现websocket,配置和项目实际使用

    依赖包: 项目目录结构: study_websocket         --study_websocket                 --__init__.py                 --settings.py                 --asgi.py                 --wsgi.py                 --urls.py         --chat                 --routing

    2024年01月18日
    浏览(37)
  • Python Django 详解(基础)

    [File] - [New Project...] - [Django] - 设置 Location 和 解释器 - [create] 固定,不用动的文件:     asgi.py:接收网络请求(异步)     wsgi.py:接收网络请求(同步) 重要,需要经常操作的文件:     urls.py:URL和函数的对应关系     settings.py:项目配置 命令窗口执行命令: python

    2024年02月07日
    浏览(36)
  • Django使用Celery异步

    安装包 1.在项目文件的根目录下创建目录结果 2. 在main.py文件中  3.config.py文件 4.在项目后端文件下执行启动命令,即可,此时说明clery已经安装成功! 5.在task.py文件中将发送短信的任务注册到task中必须使用装饰器并在装饰器中设置别名便于区分 6.在视图函数中调用异步任务

    2024年02月22日
    浏览(43)
  • Python Django 之全局配置 settings 详解

    对应项目下的 apps.py 文件,如: 在根目录下添加 templates 文件夹,可实现跳转至对应名称的 HTML 页面 方式1:项目内创建 static 文件夹 方式2:项目外创建 static 文件夹

    2024年02月08日
    浏览(39)
  • Python Django 之模板继承详解(extends)

    模板继承 和 类继承 的目的是一样的,都是为了提高代码的复用 登录效果:

    2024年02月06日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包