Talk Notes: Andrew Goodwin on Django Channels for the Real Time Web

Andrew Godwin is a Django core developer who works at Eventbrite. In this talk he talks about Django for the Real-Time web, otherwise known as Django Channels.

The Traditional Method

The "old" way of sending and receiving requests, pre-WebSockets and Django Channels
The “old” way of sending and receiving requests, pre-WebSockets and Django Channels

Send a request, get a response. Even with HTTP2, you can still treat your code the same way in a WSGI-style request.

However, with Websockets, things change. You can send without receiving, receive without sending, leave sockets open for hours, whatever. It’s the “wild wild west.” In Andrew’s mind, the way Django should work with wesockets should follow the standard Django contract: Easy to use, secure by default, hard to break / deadlock, Python 2 & 3 compatible, and optional.

But… There Are Problems.

Python is… not good with concurrency, and Django is not asynchronous. At first glance, it might seem like the solution is something like message-passing via WSGI. However, WebSockets also have the additional features of events and broadcasting, which would require cross-thread or even cross process communication.

Enter Django Channels: Concepts

Channels sits between your user interface and Django and provides an asynchronous layer utilizing WebSockets.

Django Channels is a WebSockets package based on a few concepts.

  • Channels: named FIFO task queues
  • Groups: named sets of channels with add/remove/send operations
  • Messages: representations for HTTP and WebSocket operations.
This is new way - send a message and receive zero or more messages. Views become Consumers. Messages can also go to Sockets or Workers.
This is new way – send a message and receive zero or more messages. Views become Consumers. Messages can also go to Sockets or Workers.

With these concepts, you get 5 simple API endpoint operations:

  1. send('channel_name', {ponies: True})
  2. receive_many(['channel_one'], ['channel_two'])
  3. group_add('group_name', 'channel_name')
  4. group_discard('group_name', 'channel_name')
  5. send_group('group_name', {ponies: True})
Much like Consumers are views, we have routers.py, which is paralleled by urls.py
Much like Consumers are views, we have routers.py, which is paralleled by urls.py

Example: Live Blog

Suppose you want a blog where the readers can get new blog posts as they are published, without refreshing.

  1. The client opens a websocket when the page is opened, and that websocket is added to a group.
  2. When the BlogPost model is saved, we send the post to that group

Fully working example available on GitHub.

Example: Chat

The simplest chat: a person types a message, everybody gets it. This example is nearly identical to the above example but instead of using the save method on a model, we simply use the ws_receive method:

  1. The client opens a websocket when the page is opened, and that websocket is added to a group.
  2. When the BlogPost model is saved, we send the post to that group

Fully working example available on GitHub.

Other Cool Stuff!

The ASGI Specification

Now that there’s a WebSocket medium for Django, we need a standard way of structuring channels and messages. Enter ASGI. This is an API specification for channel layer backends, as well as a message format for HTTP and WebSockets. ASGI is perfectly compatible with WSGI, and a number of other technologies as well.

Scaling?

Interface servers scale horizontally, as do worker servers. Thus, the channel layer has to as well. Luckily Django Channels has consistent hash sharding built in. Andrew talks about how it will be part of Django soon, but it’s not quite mature enough yet.

Leave a Reply

Your email address will not be published. Required fields are marked *