如何使用Django Channels使用多个websocket连接?

时间:2018-09-21 15:05:45

标签: django websocket django-channels

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

我遇到的错误是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连接/端口。我的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通道提供两个不同的Websocket连接?可能吗?还是我只是配置不正确?

2 个答案:

答案 0 :(得分:5)

根据其实现和记录(here),ProtocolTypeRouter的值是一个map / dict,他们监听或查看的全部是两种类型的键:

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

一个事实是,如果您传递websocket02之类的不同密钥,将无法使用。显然,这意味着必须有一种方法可以通过相同的websocket组合两个APP网址并创建单独的端点。

实际上,您可以做的是,就像他们提到的那样:

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),
        ])
    ),
})

OR

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

答案 1 :(得分:0)

我在寻找类似解决方案时遇到了这种情况。如果我了解到目前为止所看到的解决方案,那么它们都需要像上面的答案(https://stackoverflow.com/a/52497306/8126390)中所述,在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 URL),因为URLRouter需要一个列表,而Django URL(include('path.to.url'))可以在项目URL文件中使用列表列表。