Django Channels介绍

 2018年5月8日 14:58   Nick王   开发    0 评论   179 浏览 

Django 版本:1.11.13
Django-Channels 版本:2.1.1
Python 版本:3.6.5

Django Channels介绍

Channels可以在Django同步核心的代码下来编写异步的代码。这使得Django不仅可以处理HTTP, 还可以处理其他一些长链接服务,比如WebSocket,MQTT,chatbots等。

它既保留了Django的同步性和易用性,又允许您选择编写代码的方式 - 同步的Django视图,完全异步或两者混合的风格。最重要的是,它提供了与Django的认证系统,会话系统等的集成,使您比以往更容易将仅限HTTP的项目扩展到其他协议。

它还将事件驱动的架构与channel layer绑定在一起,该系统允许您轻松地在进程之间进行通信,并将项目分为不同的进程。

Scopes 和 Events

Channels会将传进来的连接分为两个组件:一个Scopes和一系列Events。

Scopes是一组关于单个传入链接的详细信息。比如Web请求的路径,WebSocket的始发IP地址。并且会在整个连接中保留。

对于HTTP,Scopes仅仅会持续一个单个的链接,但是对于WebSocket会持续整个生命周期,如果socket关闭重新连接,则会改变。

在Scopes的生命周期期间,会发生一系列的Events。这些代表用户交互。

HTTP示例:

  • 用户发出一个HTTP请求。

  • 我们打开一个新的HTTP类型的Scopes,其中包含请求路基、方法、头等详细信息。

  • 我们发送一个http.requestEvent,和HTTP body content。

  • Channels或者ASGI应用程序会处理这些事件并且生成http.response事件,并发送给浏览器,然后关闭链接。

  • HTTP请求/响应已完成,销毁Scopes

chatbot的例子:

  • 用户向chatbot发送第一条信息。

  • 这将打开一个包含用户名,用户ID等的Scope

  • 该应用程序被赋予一个chat.received_message事件和事件文本。它不必回应,但可以发送一个,两个或更多其他聊天消息作为chat.send_message事件,如果它想。

  • 用户发送更多的消息给chatbot和更多的chat.received_message事件被生成

  • 在超时或者应用程序重新启动之后,Scope将会销毁

在一个Scope的生命周期中,您将有一个应用程序实例来处理来自它的所有的事件。并且您还可以将其保存到应用程序实例中。

什么是消费者?

消费者是Channels代码的基本单元。这里称为是消费者,是因为它消耗Event,如果愿意的话,你可以认为这是一个小的应用程序。当一个请求或者新的Socket进入的时候,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

每种不同的协议都会有不同类型的Event发生,并且每种类型都会有不同的方法表示。您编写处理每个事件的代码,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",
        })

路由和多种协议

你可以将多个消费者组合到一个大的应用中,该应用程序使用路由表现您的程序:

application = URLRouter([
    url(r"^chat/admin/$", AdminChatConsumer),
    url(r"^chat/$", PublicChatConsumer),
])

Channels不仅仅是围绕HTTP和WebSocket的,还可以用它来实现其他,比如下面是chatbot:

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和Chat请求:

application = ProtocolTypeRouter({

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

    "telegram": ChattyBotConsumer,
})

跨进程通信

很像标准的WSGI服务器,处理协议事件的应用程序代码在服务器进程本身内运行 - 例如,WebSocket处理代码在WebSocket服务器进程中运行。

您的整个应用程序的每个套接字或连接都由这些服务器中的应用程序实例处理。他们被调用并可以直接发送数据给客户端。

但是,当您构建更复杂的应用程序系统时,您需要在不同的应用程序实例之间进行通信。例如,如果您正在构建聊天室,则当一个应用程序实例接收到传入消息时,需要将其分发给代表任何其他实例聊天室里的人。

channel layer围绕一组传输的一个低级别的抽象,允许您在不同的进程之间发送消息。每个应用程序实例都有一个唯一的Channels名称,并可以加入Group,允许点对点和广播消息

channel layer是Channels 的可选部分,如果不需要,可以给CHANNEL_LAYERS设置为空来禁用。

您还可以将消息发送到正在侦听其自己的固定通道名称的专用进程:

# In a consumer
self.channel_layer.send(
    "myproject.thumbnail_notifications",
    {
        "type": "thumbnail.generate",
        "id": 90902949,
    },
)

Django 集成

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




如无特殊说明,文章均为本站原创,转载请注明出处
  • 转载请注明来源:Django Channels介绍
  • 本文永久连接地址: http://ibash.cc/frontend/article/94/