Django/channels

介绍

欢迎来到channels。

Channels 包装了 Django 的原生异步视图支持,允许 Django 项目不仅可以处理 HTTP,还可以处理需要长时间运行的连接的协议——WebSockets、MQTT、聊天机器人、业余无线电等等。

它这样做的同时保留了 Django 的同步和易于使用的特性,允许您选择编写代码的方式 - 以 Django 视图等风格同步、完全异步或两者兼而有之。 最重要的是,它提供了与 Django 的身份验证系统、会话系统等的集成,使您比以往任何时候都更容易将仅 HTTP 的项目扩展到其他协议。

Channels 还将这种事件驱动架构与channel层捆绑在一起,该系统允许您轻松地在进程(processes)之间进行通信,并将您的项目分成不同的进程(processes -- 这里是指进程么?)。

如果您还没有安装 Channels,您可能需要先阅读安装来安装它。 这个介绍不是一个直接的教程,但如果你愿意,你应该能够使用它来跟随并更改现有的 Django 项目。

海龟一路向下

Channels 以“海龟一路向下”的原则运行——我们对什么是channels“应用程序”只有一个概念,即使是最简单的consumers(相当于 Django 视图)也是一个完全有效的 ASGI 应用程序,您可以run by themselves。

Note:

ASGI 是构建 Channels 的异步服务器规范的名称。 与 WSGI 一样,它旨在让您在不同的服务器和框架之间进行选择,而不是被锁定在 Channels 和我们的服务器 Daphne 中。 您可以在 ASGI Documentation 了解更多信息。

Channels 为您提供了编写这些基本消费者的工具 - 可能处理聊天消息或通知的单独部分 - 并将它们与 URL 路由、协议检测和其他方便的东西结合在一起以构成完整的应用程序。

我们将 HTTP 和现有的 Django 应用程序视为更大整体的一部分。 传统的 Django 视图仍然存在 Channels 并且仍然可用 - 使用 Django 的本机 ASGI 支持,或为 Django 2.2 提供的 Channels 版本 - 但您现在也可以编写自定义 HTTP 长轮询处理或 WebSocket 接收器,并将该代码放在您现有代码的旁边 。 URL 路由、中间件——它们都只是 ASGI 应用程序。

我们的信念是,您希望能够为大多数代码使用安全、同步的技术(如 Django 视图),但可以选择使用更直接的异步接口来处理复杂的任务。

Scopes和Events

Channels和 ASGI 将传入连接分成两个组件:一个作用域和一系列事件。

Scopes是关于单个传入连接的一组详细信息 - 例如发出 Web 请求的路径,或 WebSocket 的原始 IP 地址,或用户向聊天机器人发送消息 - 并在整个连接中持续存在。

对于 HTTP,Scope仅持续一个请求。 对于 WebSocket,它会在套接字的整个生命周期内持续存在(但如果套接字关闭并重新连接,则会发生变化)。 对于其他协议,它根据协议的 ASGI 规范的编写方式而有所不同; 例如,聊天机器人协议可能会为用户与机器人的整个对话保持一个范围打开,即使底层聊天协议是无状态的。

在此scope的生命周期内,会发生一系列event。 这些代表用户交互——例如发出 HTTP 请求或发送 WebSocket 帧。 您的 Channels 或 ASGI 应用程序将在每个范围内实例化一次,然后接收在该范围内发生的事件流以决定如何处理。

HTTP 示例:

用户发出 HTTP 请求。
我们开辟了一个新的 http 类型的scope,其中包含请求的路径、方法、标头等详细信息。
我们发送一个带有 HTTP 正文内容的 http.request 事件
Channels 或 ASGI 应用程序对此进行处理并生成一个 http.response 事件以发送回浏览器并关闭连接。
HTTP 请求/响应完成,作用域被销毁。

一个聊天机器人的例子:

用户向聊天机器人发送第一条消息。
这将打开一个Scope,其中包含用户的用户名、选择的名称和用户 ID。
应用程序会收到带有事件文本的 chat.received_message 事件。 它不必响应,但可以根据需要将一个、两个或多个其他聊天消息作为 chat.send_message 事件发送回来。
用户向聊天机器人发送更多消息,并生成更多 chat.received_message 事件。
超时后或应用程序进程重新启动时,作用域将关闭。

在一个范围的生命周期内——无论是聊天、HTTP 请求、套接字连接还是其他东西——你将有一个应用程序实例处理来自它的所有事件,并且你也可以将事情持久化到应用程序实例上。 如果您愿意,您可以选择编写原始 ASGI 应用程序,但 Channels 为您提供了一个易于使用的抽象,称为消费者(Consumers)。

什么是消费者?

消费者是 Channels 代码的基本单位。 我们称它为消费者,因为它消费事件,但您可以将其视为它自己的小应用程序。 当一个请求或新的套接字进来时,Channels 将遵循它的路由表——我们稍后会看一下——为该传入连接找到正确的消费者,并启动它的副本。

这意味着,与 Django 视图不同,消费者是长期运行的。 它们也可以是短期运行的——毕竟,HTTP 请求也可以由消费者提供服务——但它们是围绕生存一段时间的想法构建的(它们在作用域的持续时间内生存,如上所述)。

一个基本的消费者看起来像这样:

class ChatConsumer(WebsocketConsumer):

def connect(self):
    self.username = "Anonymous"
    self.accept()
    self.send(text_data="[Welcome %s!]" % self.username)

def receive(self, *, text_data):
    if text_data.startswith("/name"):
        self.username = text_data[5:].strip()
        self.send(text_data="[set your username to %s]" % self.username)
    else:
        self.send(text_data=self.username + ": " + text_data)

def disconnect(self, message):
    pass

每个不同的协议都有不同类型的事件发生,每种类型由不同的方法表示。 您编写处理每个事件的代码,Channels 将负责调度它们并并行运行它们。

在底层,Channels 运行在一个完全异步的事件循环上,如果你像上面那样编写代码,它将在同步线程中被调用。 这意味着您可以安全地执行阻塞操作,例如调用 Django ORM:

class LogConsumer(WebsocketConsumer):

def connect(self, message):
    Log.objects.create(
        type="connected",
        client=self.scope["client"],
    )

然而,如果你想要更多的控制并且你愿意只在异步函数中工作,你可以编写完全异步的消费者:

class PingConsumer(AsyncConsumer):
async def websocket_connect(self, message):
await self.send({
"type": "websocket.accept",
})

async def websocket_receive(self, message):
    await asyncio.sleep(1)
    await self.send({
        "type": "websocket.send",
        "text": "pong",
    })

您可以在 Consumers 中阅读更多关于消费者的信息。

路由和多种协议

您可以将多个consumer(记住,他们自己的 ASGI 应用程序)组合成一个更大的应用程序,使用路由来代表您的项目:

application = URLRouter([
url(r"^chat/admin/$", AdminChatConsumer.as_asgi()),
url(r"^chat/$", PublicChatConsumer.as_asgi(),
])
Channels 不仅仅是围绕 HTTP 和 WebSockets 构建的——它还允许您将任何协议构建到 Django 环境中,方法是构建一个将这些协议映射到一组类似事件的服务器。 例如,您可以构建一个类似风格的聊天机器人:

class ChattyBotConsumer(SyncConsumer):

def telegram_message(self, message):
    """
    Simple echo handler for telegram messages in any chat.
    """
    self.send({
        "type": "telegram.message",
        "text": "You said: %s" % message["text"],
    })

然后使用另一个路由器让一个项目能够同时服务 WebSocket 和聊天请求:

application = ProtocolTypeRouter({

"websocket": URLRouter([
    url(r"^chat/admin/$", AdminChatConsumer.as_asgi()),
    url(r"^chat/$", PublicChatConsumer.as_asgi()),
]),

"telegram": ChattyBotConsumer.as_asgi(),

})

...

与Django 集成

Channels 附带了对常见 Django 功能(如会话和身份验证)的简单支持。 您可以通过在它们周围添加正确的中间件来将身份验证与 WebSocket 视图结合起来:

from django.urls import re_path
from django.core.asgi import get_asgi_application

from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack

application = ProtocolTypeRouter({
"http": get_asgi_application(),
"websocket": AuthMiddlewareStack(
URLRouter([
re_path(r"^front(end)/$", consumers.AsyncChatConsumer.as_asgi()),
])
),
})****

posted @   krysatl  阅读(129)  评论(0编辑  收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示