Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use multiple websocket connections using Django Channels?

I have been happily using Django-Channels for several months now. However, I went to add a second websocket dependent application to my Django project and I am running into trouble.

The error I am getting is websocket connection failed websocket is closed before the connection is established. What is odd is that the first application was working before the second application was deployed. Further, the first application continues to work so long as the second application is not running.

The Django Channels documentation says:

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.

I think this means that Django-Channels does not support routing for multiple websocket connections. That is, I think I am trying to use the same websocket connection/port for two different applications. My routing.py file looks as follows:

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

When I attempted to use the setup below, it could not find the path to the first application:

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

})

How can I setup my Django application to serve up two different websocket connections using Django-Channels? Is it possible? Or am I just configuring things improperly?

like image 365
Scott Skiles Avatar asked Sep 21 '18 15:09

Scott Skiles


People also ask

How many connections can Django channels handle?

Websockets go into a server called Daphne (Daphne is a HTTP, HTTP2 and WebSocket protocol server for ASGI and ASGI-HTTP, developed to power Django Channels) can handle hundreds or potentially thousands of simultaneous connections open at once.

Can you open multiple WebSocket connections?

A server can open WebSocket connections with multiple clients—even multiple connections with the same client. It can then message one, some, or all of these clients. Practically, this means multiple people can connect to our chat app, and we can message some of them at a time.

Is Django good for WebSockets?

Django Channels facilitates support of WebSockets in Django in a manner similar to traditional HTTP views. It wraps Django's native asynchronous view support, allowing Django projects to handle not only HTTP, but also protocols that require long-running connections, such as WebSockets, MQTT, chatbots, etc.

Is there a limit on number of WebSocket connections?

Using a WebSocket library of your choice to connect to the server. After the 10th connection no more connections are accepted by the server.


2 Answers

According to their implementation and documention (here), the value to the ProtocolTypeRouter is a map/dict and all they listen or view for is two types of keys:

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

Which brings to the fact that if you pass different key like websocket02 it will not work. Which would obviously mean there has to be a way to combine both the APP url's via same websocket and create separate endpoint.

Infact, what you can do is, something like they have mentioned:

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

})

The above example is based on their implementation for two endpoints (here):

application = ProtocolTypeRouter({

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

OR

route based on different channels under same websocket: https://github.com/django/channels/blob/master/docs/topics/routing.rst#channelnamerouter

like image 162
Nagaraj Tantri Avatar answered Nov 01 '22 08:11

Nagaraj Tantri


I came across this SO while searching for a similar solution. If I'm understanding the solutions I've seen thus far, they would all require specifying the consumers in the Django project routing file like mentioned in the answer above (https://stackoverflow.com/a/52497306/8126390):

application = ProtocolTypeRouter({

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

If you want to extend the tutorial while maintaining a similar structure to the tutorial, you can do the following:

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

This allows you to leaving routing for particular URLs to particular consumers in the app, rather than project routing file. The tutorial simply points to chat.routing.websocket_urlpatterns which is a list of paths or urls directly. You can concatenate these lists to build your overall project routing structure which is what I show above with +.

I'm assuming this is required (and different from Django URLs) because URLRouter is expecting a single list, while Django urls (include('path.to.url')) are okay with a list of lists in the project URLs file.

like image 33
Brian Avatar answered Nov 01 '22 10:11

Brian