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?
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.
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.
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.
Using a WebSocket library of your choice to connect to the server. After the 10th connection no more connections are accepted by the server.
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With