如何使用 Django 通道使用多个 websocket 连接?

How to use multiple websocket connections using Django Channels?

我已经愉快地使用 Django-Channels 几个月了。但是,我去向我的 Django 项目添加了第二个依赖于 websocket 的应用程序,我 运行 遇到了麻烦。

我收到的错误是 websocket connection failed websocket is closed before the connection is established。奇怪的是,第一个应用程序在部署第二个应用程序之前就在运行。此外,只要第二个应用程序不是 运行,第一个应用程序就会继续工作。

Django Channels documentation 说:

Channels routers only work on the scope level, not on the level of individual events, which means you can only have one consumer for any given connection. Routing is to work out what single consumer to give a connection, not how to spread events from one connection across multiple consumers.

我认为这意味着 Django-Channels 不支持多个 websocket 连接的路由。也就是说,我想我正在尝试对两个不同的应用程序使用相同的 websocket connection/port。我的 routing.py 文件如下所示:

application = ProtocolTypeRouter({
    "websocket": AuthMiddlewareStack(
        URLRouter([
            path("first_application/stream/", app_1_consumers.AsyncApp1),
            path("second_application/stream/", app_2_consumers.AsyncApp2),
        ])
    )
})

当我尝试使用下面的设置时,找不到第一个应用程序的路径:

application = ProtocolTypeRouter({
    "websocket": AuthMiddlewareStack(
        URLRouter([
            path("second_application/stream/", app_2_consumers.AsyncApp2),
        ])
    ),
    "websocket02": AuthMiddlewareStack(
        URLRouter([
            path("first_application/stream/", app_1_consumers.AsyncApp1),
        ])
    ),

})

如何设置我的 Django 应用程序以使用 Django-Channels 提供两个不同的 websocket 连接?可能吗?或者我只是配置不当?

根据他们的实施和文档 (here),ProtocolTypeRouter 的值是 map/dict,他们只听或查看两种类型的键:

ProtocolTypeRouter({
   "http": some_app,
   "websocket": some_other_app,
})

这导致如果您传递不同的密钥,如 websocket02,它将不起作用。这显然意味着必须有一种方法可以通过相同的 websocket 组合两个应用程序 url 并创建单独的端点。

事实上,你能做的就是像他们提到的那样:

from django.conf.urls import url

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

application = ProtocolTypeRouter({

    # WebSocket chat handler
    "websocket": AuthMiddlewareStack(
        URLRouter([
            url(r"^first_application/stream/$", app_2_consumers.AsyncApp1Consumer),
            url(r"^second_application/stream/$", app_2_consumers.AsyncApp2Consumer),
        ])
    ),

})

以上示例基于两个端点 (here) 的实现:

application = ProtocolTypeRouter({

    # WebSocket chat handler
    "websocket": AuthMiddlewareStack(
        URLRouter([
            url(r"^chat/admin/$", AdminChatConsumer),
            url(r"^chat/$", PublicChatConsumer),
        ])
    ),
})

同一websocket下基于不同通道的路由:https://github.com/django/channels/blob/master/docs/topics/routing.rst#channelnamerouter

我在寻找类似的解决方案时遇到了这个 SO。如果我理解到目前为止所看到的解决方案,它们都需要在 Django 项目 routing 文件中指定使用者,如上面的答案中所述 ():

application = ProtocolTypeRouter({

    # WebSocket chat handler
    "websocket": AuthMiddlewareStack(
        URLRouter([
            url(r"^chat/admin/$", AdminChatConsumer),
            url(r"^chat/$", PublicChatConsumer),
        ])
    ),
})

如果您想扩展本教程,同时保持与本教程相似的结构,您可以执行以下操作:

application = ProtocolTypeRouter({
    # (http->django views is added by default)
    'websocket': AllowedHostsOriginValidator(
        AuthMiddlewareStack(
            URLRouter(
                # Original chat connection
                chat.routing.websocket_urlpatterns +
                # Second chat connection
                chat2.routing.websocket_urlpatterns
            )
        ),
    ),
})

这允许您将特定 URL 的路由留给应用程序中的特定消费者,而不是项目 routing 文件。本教程简单地指向 chat.routing.websocket_urlpatterns,它是直接 pathsurls 的列表。您可以连接这些列表来构建您的整体项目路由结构,这就是我上面用 +.

显示的结构

我假设这是必需的(并且与 Django URLs 不同),因为 URLRouter 需要一个列表,而 Django urls (include('path.to.url')) 对于项目 URLs 文件中的列表列表是可以的。