Keynote: Architecting with Channels by Andrew Godwin

This video features Andrew Godwin at DjangoCon US 2016 in Philadelphia, Pennsylvania, USA.

Keynote: Architecting with Channels by Andrew Godwin
0:44:35
Published August 14, 2016
1,400 views

Keynote: Architecting with Channels by Andrew Godwin
Going through Websockets and Channels, and the 'real hard problem' of asynchoronous coordination.

This talk was presented at: https://2016.djangocon.us/schedule/general-sessions/

LINKS:
Follow DjangCon US 👇
https://twitter.com/djangocon

Follow DEFNA 👇
https://twitter.com/defnado
https://www.defna.org/

Summary

Andrew Godwin argues that Django Channels should be understood not merely as a way to add WebSockets, but as a coordination layer for messaging across processes and servers. WebSockets are useful for bidirectional, low-overhead applications such as chat, live updates, collaborative editing, and games, but their difficult problem is coordinating stateful connections and broadcasts across a cluster. Channels addresses this through a channel layer with point-to-point sends, groups, routing, consumers, sessions, authentication, ordering, and pluggable backends such as Redis or shared memory. The same abstraction can support task offloading, data binding, email, Slack-like services, MQTT, schedulers, and custom protocol servers, while allowing connection-handling and application workers to scale independently.

Key takeaways

  • WebSockets suit bidirectional, low-overhead applications such as chat, live blogs, collaborative editing, and game backends, but they do not replace ordinary HTTP.
  • The hard part of real-time systems is coordinating broadcasts and stateful connections across multiple servers, not terminating individual WebSocket connections.
  • Channels provides channels, groups, routing, consumers, sessions, authentication, ordering, and pluggable backends to hide this distributed-systems complexity.
  • Daphne handles HTTP and WebSocket connections separately from channel workers, so deployments can scale connection handling and application work independently.
  • The channel abstraction can connect Django to task workers, email, Slack-like services, MQTT devices, schedulers, and other custom protocols.
  • Higher-level features such as data binding and multiplexing can let model changes flow to connected clients with very little application code.

Summarised automatically from the transcript.

Chapters

  1. 0:00 Introduction to Channels Andrew Godwin introduces Django Channels and frames the talk around its broader purpose beyond WebSockets.
  2. 2:00 WebSockets and Channels History The talk traces the speaker’s early experiments with WebSockets, Meteor, and the evolution toward Channels.
  3. 5:55 The Real Hard Problem The speaker distinguishes simple WebSocket handling from the harder problem of asynchronous coordination across servers.
  4. 6:41 WebSocket Use Cases The talk reviews the trade-offs of WebSockets and the applications that benefit from low-overhead, bidirectional communication.
  5. 9:01 Broadcasting Across Servers Broadcast is presented as the common requirement behind chat, collaboration, and other real-time applications.
  6. 13:00 Scaling and Network Coordination The speaker examines stateful connections, network saturation, bottlenecks, and the challenges of scaling real-time systems.
  7. 14:37 The Channel Layer Channels’ core send, receive, and group operations are explained as a foundation for routing messages across processes and machines.
  8. 19:15 Django Integration The talk introduces ASGI, Daphne, pluggable backends, routing, consumers, sessions, authentication, and worker commands.
  9. 24:42 Building a Chat Application A concise chat implementation demonstrates how consumers and groups simplify WebSocket development in Channels.
  10. 26:59 Data Binding and Multiplexing The speaker demonstrates higher-level features for sending multiple streams over one WebSocket and synchronizing model changes.
  11. 30:58 Beyond WebSockets Channels is extended conceptually to email, Slack-like messaging, MQTT, and other protocol integrations.
  12. 34:54 Custom Protocol Servers The talk explains how asynchronous protocol processes can connect to Django through the channel layer and be deployed across specialized workers.

Transcript

8,875 words · auto-generated Show

Automatically transcribed, so expect mistakes in names and technical terms.

0:00

Speaker 1: Come on,

0:00

Speaker 2: yo, yeah, yeah, yeah, yeah.

0:14

Speaker 3: So today's speaker is Andrew Godwin. Um he is a Django core developer. He works for Eventbrite, the T ticketing system as a senior software engineer. You're probably very familiar with him if you ever use the package South, which then turned into migrations in Django 17. And now he's working on channels. Um he as you heard yesterday, or as you saw Jeff, point to two people towards the front of the room who like to attend Django Cons almost religiously, it seems. They have been to everyone. And this will mark Django uh Andrew's ninth Django Con, i. e. everyone in the US, and the ninth time he has spoken at one as well So his talk, my alternative title for this talk is uh Teaching a Pony New Tricks. And he is going to speak to us about architecting channels. So Andrew, the floor is yours.

1:13

Speaker 4: There we go. Fantastic. Thank you very much, Craig. And hello everybody. Hope you had a good night last night and good morning. So I am here to talk to you about channels and to warm you up a bit of light audience participation. Who here has heard of channels at all? It's about I say ninety five percent of the room. Who here has used web channels so far? It's about twenty people. And who here has used WebSockets at all in any capacity? That's about I say half uh one third So, yes, as I said before, I'm Andrew Godwin. I'm a Django core developer. I'm a senior software engineer at Eventbrite as well. And in a past life, i. e. about two years ago and backwards, I did a lot of migrations work. And what I want to talk to you today is entirely unrelated, it's about channels, but first

2:00

Speaker 4: a bit of history that mixes in with that. So in 2010 I was deep in the throes of Django development and also at this point not a Django core developer. And this is when I first encountered WebSockets. And And I sort of ran across them, I think randomly. They were still in development at this point. The spec wasn't finalized, it kept changing, they kept like fiddling the security features. And my first exposure was sort of in an attempt to try and do like games programming on the web or some kind of interactive site. And this is still very early in the web's development for me at least. And part of that was sort of like, well I wasn't sure what was going on. I wasn't sure how Python would work with it, but I was trying to figure out a way to run Python with WebSockets. And if you saw the talk yesterday about channels, you know that Python by itself is not very well designed for WebSockets.

2:48

Speaker 4: It's a protocol that encourages long-lived, stateful connections on the orders of thousands per machine. Python doesn't have a good threading model that matches that. Like Python has threading, but it's not particularly efficient at that kind of scale. And so my first port of call was eventl at the time. GEvent is now more popular, I would say, but at the time Eventlet was quite popular too. And I sort of took to the Eventlet, they had some old topic code. I went in there and fixed it up. Like I have like three commits back in 2010 on the code base for Eventlet for this one thing And I fiddled around with it for a bit and it was it was quite fun. I got some stuff working, but there was no real need for it. I couldn't see the use for it. And so, you know, time moved on. Come 2012 and the thing that happened that really sort of piqued my interest uh was the answer of meteor.

3:34

Speaker 4: So who here has heard of meteor? Ends up 60%. view. So Meteor is a JavaScript-based framework that basically has sort of native data binding interface. Like you can make a website and then just drag things around on one screen, they just mysteriously rearrange on the other screen. And this is just a feature that comes with it by default. And I saw this and I was like, oh, there's a good use of WebSocket. Like there's finally a sort of good demonstration of what they meant. And Meteor itself has grown and gone since then, but it is still in it is still in JS. It does use Mongo as its backing data store, and I have opinions on MongoDB that I I'm sure you can imagine what they are. And so while while it's a lovely idea, um it's not quite what I was looking for for Django, obviously.

4:22

Speaker 4: I am coming towards the end of the migrations project in Django, like the moving south. into Django as it were. That actually was a full rewrite, but still was kind of moving the concept in. And so again, like I had a bit of free time, which is kind of a rarity at this point. Think, well what what what what interests me next? What am I looking for And so at this point I started looking at in sort of full detail what would it mean for Django to do WebSockets? Like what was the what would the usage be? How do we even implement it? Is it even impossible. And so my first version of this was codename Django on Ed, but this has never been released. It sits on a folder on my machine somewhere, I should probably dig it out. And this was a very early prototype of what a meteor-ish Django would look like. It use a external server to do WebSocket

5:08

Speaker 4: termination. This is very common in Django currently. It wasn't particularly great. The syntax was awful. It ran particularly slowly, but I liked the idea. And then the following year, 2015, I sort of kept going with the idea and refined it, sort of rewrote the idea, and then 2015 is when I first announced channels. And if you want to know what channels is in full detail, um the talk from yesterday or my talk from PyCon is is a good move for that. And this talk is much more about like what is channels aiming for, what is the vision of channels, and like what does it even mean? Because a lot of people think That channels is just for WebSockets. That is, it is a means to bring WebSockets to Django, then we're done, move on, everything's fine.

5:55

Speaker 4: That's not true. Channels was indeed born from things like Django on Air, my attempt to bring WebSockets into Django, but it has become much bigger than that. And like today I hope to show you a bit more of what the scope is and what the possibilities are with channels. So underlying all of this is what I call the real hard problem. So if you talk some of my talk at PyCon, I touched on this briefly, but today I want to go into it in a bit more detail. So a lot of people think, well, WebSockets, they're not that they're not that hard. We just, you know, we'll bring in asyncaio or something, we'll tie in an event loop. Then we'll just do socket termination. Autobarn is a great library for that. I recommend it heavily. What's the problem there? Like, you know, it's it's tricky, but we can engineer it all right. And I agree, that bit is very easy.

6:41

Speaker 4: But that's not the real hard problem. The real hard problem is asynchronous coordination. And I'll explain what that is in a bit more detail. But before I do that, I want to first look at what do you use WebSockets for. Because a lot of this, what I'm about to chow to explain to you, is not driven from an initial design, it's driven from a revised design of designing a thing, trying it out with ideas and examples and real projects and realizing that the problem that I solved initially wasn't the hard problem. The hard problem was hiding somewhere underneath. So why do you use WebSockets? Well, first of all, you don't always use them. They're not some kind of magical panacea that solves every problem you might have. They're they're pretty great, I think, personally, but they won't solve like your standard like rendering the page issues and that kind of stuff.

7:28

Speaker 4: And they're an architectural consideration. An architecture is all about trade-offs If anybody ever tells you that a piece of software or a solution fixes all your problems, they are lying to you. Don't listen to them. Architecture is always about a set of trade-offs. And so for WebSockets, you have things like this. This is obviously not exhaustive But WebSockets are bi-directional, so unlike a normal request, you can send things both ways very easily. Like a request response in an HTTP is just one way and then one way back. They're low overhead. Like, unlike if you do like sort of very frequent HTTP polling, WebSockets wanted to open a very small amount of data to send and receive packets. However, on the flip side, they're quite complex to set up. Like you have to have JavaScript terminating them on the client side. And moreover, they're not supported in every single browser.

8:15

Speaker 4: These days, support's a lot better. I think a couple of mobile browsers are the last holdouts of the current versions, but there's still a few issues with there and with proxies and firewalls and things like that. And so with this in mind You still want to use normal HTTP for things like rendering pages and serving images and all this kind of stuff. And WebSock are designed for a subset of applications that really benefit from very like what what is incorrectly I kind of termed real-time updates. So things like streaming updates for a live blog, things like chat applications, collaborative editing, things like, you know, like if you ever use Google Docs, like that kind of editing is is best done via WebSockets. Game backends, my very first interest from the sockets 2, these all benefit from very heavy, very chatty real-time communication between a server and a web browser.

9:01

Speaker 4: It's very much sort of the next step in web applications. Like it's no longer click, submit a request, wait for response, bang. It's more like, no, we can not reload the page and tie these things in dynamically. And these days, things like React and Backbone and Angular growing, it's even easier than ever to do this. And if you look at these particular applications, you realize that something fundamental underlies almost all of them, and that's broadcast. So this is where one client or maybe a database or whatever makes an event. So say in the chat example, I send a message to the chat. And what has to happen is everybody else Who's listening on the chat has to receive that message. And so that's a broadcast operation. It's taking one input and giving it to all the connected clients If you have the simple server that has, well, we have an event loop, we have autobar and async.

9:51

Speaker 4: It's very trivial. You just say, okay, for every connection I have, send it over. That's not the hard problem. The hard problem is when you go to two servers And then suddenly there's a mysterious gap between the two servers where you have to go, well, I need to send something to one server and the other server has to mysteriously know it happened and then work out if it needs to send it and then send it to all its clients if they need to have it themselves. And that is what I call the real hard problem. Like taking a simple example locally is easy enough. Scaling it to a cluster of 10, 20, 30 servers that have to coordinate, that's really difficult. And that's the problem I ran up against in 2014 with OnAir that I was like, we need to step back and solve this problem more directly. And so channels is not just a WebSocket solution.

10:37

Speaker 4: The underlying key part of channels is it's a foundation for running services like this at scale. It's the underpinnings of a system that lets you solve the hard problem because I believe frameworks are there to solve the hard problems for you and let you do the easy fun problems instead. Like as a programmer, that's what I want personally. I don't want to do hard work. And so, yeah. Who was? Lazy programmers is the best thing. And so channels is is trying to provide that underlying layer, right? The idea is to give you the solution to the server coordination problem. So that you can get on with your application and say that oh making a chat is five light ins. As you'll see later it's six lines, but we'll get there. And If you think about what this means, and I'll show you in the rest of this talk, this actually brings a lot more than WebSockets. So WebSockets are one of the obvious uses for this.

11:23

Speaker 4: But there's also things like task offloading. So I'm sure a lot of you use Celery. Celery is one version of this and Celery is very good for things that must run at some point. Channels offers a very similar functionality for things that should sh probably should run straight away. So it's a very sort of different guarantees version of that. But things that you would perhaps run during a request normally, you can offload them just to a channel and another server will just pick them up and be because of the coordination, you can have another server entirely separate doing that as well as the server you're currently on, or different processes. And not only that, chat and email integration. So one of the flexibilities of channels is it it's written around a generic protocol abstraction. And it's an invented system. And so, sure, there's events for WebSockets for sending and receiving things, connecting and disconnection, but you can tie in things like, oh, I got a chat message, or send a chat message, or

12:14

Speaker 4: I received an email. And similarly, you can inject these into a cluster of servers that runs channels and they get handled correctly. Other things that are very popular, things like Internet of Things protocols. MQTT is a popular example here. If you are in the industry, that made sense to you. If not, I'm sorry. But these are things where like small embedded devices talk specialized, like low bandwidth protocols to servers. Django, again isn't very good at understanding these because it's so tied to this HTTP request response model. And so part of channels is breaking open that seal. So I said it's a hard problem. I'm sure some of you believe me. Thank you for trusting in me. But let's look at why, because I like I like explaining things a bit more detail. So when one is faced with scaling WebSockets in particular, um

13:01

Speaker 4: they are possibly the worst case of network engineering scaling. They are stateful, which means that they open and sit that open for potentially days at a time. Like, you know. You can't just load balance them across servers randomly because if everyone connects at like 8 p. m. for like some kind of sports game and then it just sits open for a while, if you rooted it written correctly, they're all on one cluster. And you can't you can't just cut them off and re-root them without cutting off the connection So that doesn't help. Not only that, you have internal network issues. So with this coordination I showed you, you could just broadcast everything multicast to the entire network that you're on, which is great until you realize that you only have like a one gigabit Ethernet cable on all the servers. And trust me, with a couple of servers you can easily saturate that because the more you add, it gets exponentially more complex than they try to send to each other.

13:50

Speaker 4: And finally you get bottlenecks. So not just in the networking, but if you say, okay, well, we'll solve the networking issue by having a centralized server to send things through. Well now, great, congratulations, you have a single point of failure. Well done, that's fantastic for you. Uh so yeah, these are all considerations that like you know and as you get through the process you hit each of these in turn and go, well, I've solved it by having a central server. Oh, it's a but it's a single point of failure. Well I've now done oh and the network set. And so this is the sort of problem that I think channels and Django in general should be solving for you because these are generally problems that benefit from very focused expertise. expertise that I do not expect most programmers to have. Like you should understand the concerns here and that that's good programming, but you shouldn't be able to sit down and write a good cross-network sharded

14:37

Speaker 4: message delivery system in a few weeks. That's not something I expect you to do generally as programmers. And so channels is built on this abstraction that lets you send things between servers. And in particular, it's a little bit more than just channels. So there are five key endpoints in channels. This is basically the entire basic API channels written as English. So you can send to a channel, you can receive from channels. So send more than one, in fact, we can send to a group, and a group is just a collection of channels, so you can add and remove them from groups. And so sending and receiving from channels is the first hard problem. So say I have a WebSocket on box A And I'm sending stuff to somebody who's clicking on box B. Channels knows that when you send to channel B to go onto the network, hop across and go to that box. It solves that routing problem for you.

15:23

Speaker 4: But moreover, if I'm sending to a room of people and they're all in a group called room A, when I send to group room A, channels knows to go to that group, expand the channel list, send the right channel message to each server, and then deliver it to all the people. And that's solved for you. That's the key part of channels. And it's not just done from Django. It's a Python-level abstraction. So yes, you can do it from Django. You can do it from the web server, which is the way so as a bit of background, channels runs as two processes usually. Daphne, which is a server that takes HTTP and WebSockets and makes them into messages on the channel layer and then Django reads off the channel layer and then executes those messages. So it's kind of a separate process. But you can have more than that. You can have script too. Like you can have a management command that when you run it

16:09

Speaker 4: sends a channel message Like if you want to have things on a scheduled task, you can just make a cron that calls a manuscript and just injects messages into channels. So it's a very flexible way you can call it from. And of course, part of Django is that Django is very good at getting out of your way or letting you change things if you want. And so the very first prototype of this was built on Redis. And the idea is that every single process has a local abstraction layer, which is codenamed ASGI. And you s that's where you call those m those APA methods on, and then they use Redis to communicate. The first version was like this It works pretty well, and this is still the basic deployment we recommend, but there are other options, and because it's Django, it's a pluggable backend. So there's Redis.

16:55

Speaker 4: If you feel like it, there's POSIX shared memory segments. Uh if you ever feel like writing POSIX IPC code, don't, is my recommendation. Uh it takes a while to get right. Um but if you want to run everything on one machine, um Shared memory is much faster than Redis because it just literally writes into a section of memory and then there's a sort of a simple lock and other things read from it. So this is a nice low barrier eventually solution for you know on a deadlock machine there's no extra Redis server to run, there's no extra overhead, it's very fast. But then if you're in production you want a bit more of an architecture. And so if you want to, say, um you can have more than one Redis because as I said before, single points of failure, very bad. And so you can have a sharded Redis and then moreover you can have sharded Redis

17:41

Speaker 4: with shared memory in front of it such that if a process is local it will go just through the memory. And if it's on a different machine it goes through the memory, into Redis, out through the other memory, into other machine. And this just is in channels right now. You can just use this. And so this is kind of the solution to, well, at scale you don't want to have everything going into the network. As I said before, that's a huge like overflow issue with the with the with your connection. But you also don't want everything on one machine. So th architectures like this let you make that balance. And moreover, the specification is A, actually fully specified. There's a real document with this and with like must and should and everything. And yeah, that's good, right? And and second of all, it is very flexible. Like the API is deliberately just enough that you can, if you want to, write a backend

18:29

Speaker 4: that fits your deployments and your needs. that still sits within the specification. The idea being that at large scale, the best person who knows about your infrastructure is you And so the flexibility is there for when you as you scale up, as you get bigger, you can take what channels provides and mutate it to your will. There's no need to be locked into a certain solution. If you're curious, we'll have a lot of time. The specification is here. It's about 20 pages long, so good luck. But it's a good Sunday night read, trust me. And so that's kind of the low-level part of channels, and that's all is codenamed ASGI. And on top of that sits this sort of like nice abstraction layer that is for Django. And so channels is very much a sort of wrapper around these lower level operations.

19:15

Speaker 4: So I showed you those five things before, like you know, send, receive, group, add, group, remove. And channels is very much the Django nicety layer around all of this stuff. It's like part of a larger hole. And to show you what that larger hole is, channels is currently five packages. Um this may seem slightly daunting slash insane, but it makes a lot of sense. So channels is the Django integration and this is what a lot of you will interact with on a daily basis and Never touch anything else on this slide. But Daphne is a separate server that understands the channels protocol. So when you actually do run server on channels, it just runs Daphne for you. It's kind of like Unicorn, but for ASGI, not WSGI. And that sort of handles all the termination of WebSockets, all the termination of HTTP, and then just sends things on a known format to the channel layer.

20:04

Speaker 4: So that's sort of how the real world is talked to. And you you can scale that part of the system, that part of the system, differently to the channels part. So if you have a lot of connections but not a lot of work, you can have a lot more Daphne 's than you have channel workers. If you have a lot of sort of work going on, very heavy web but very few connections, you can scale the workers and not Daphne. So it's fairly flexible in that sense. Then underneath those sit the channel implementation. And so the bottom three things here, ASI Redis is the Redis backend I mentioned before. ASE IPC is the posit shared memory backend. And ASGI ref is a common thing they both inherit from that also carries an in-memory backend for like unit tests and that kind of stuff. And together they are just what you basically import into the project and settings for

20:49

Speaker 4: like settings use the Reddit's back end host this is the host list encryption key here go and it solves the rest of it for you If you want to, you can subclass them and write your own. They're packages because you can write your own one. If you want to write one based on AMQP or some other format, feel free. And so what does channels provide? Like what is this wrapper in Django giving you? So, first of all, it gives you routing. And so when you send things on channels, channels have names. So like you know when I connect to a socket, it's you I get a message on WebSocket. connect. When I send a message to a Django instance, I get WebSocket. receive. And by default, the low-level API just lets you receive messages. Says, oh yeah, you've got a message on receive content was this. What channels provides is a routing layer.

21:35

Speaker 4: So like you say, well, I have a URL's Py-like file called routing. py. They say, well, for this channel, call this function. Or for connections on this path, call this function. And so not only can you root different channels different places, but you can root by URL path, you can root by other attributes in the message If you want to, you can root by method. I'm not sure why you'd want to, but you can. And so it's a very flexible way of doing all this stuff. And as we'll see later on in the presentation, it is useful beyond incoming messages. Consumers, which is a standardized way of writing message handling. So the idea with channels is very much to be as familiar and quote unquote Django-ish as possible. And so a consumer is a standardized way of handling messages that looks just like a view. A view takes a request and returns a response.

22:22

Speaker 4: A consumer takes a message and sends zero more messages. That's all they do. And you you tie them into routing, like you tie views into urls. py. And Django handles the event looping for you, handles all the management, handles error handling, handles debugger logging, all of that's done for you. And all you have to write is these nice single contained functions of when you get a WebSocket message, do this. When someone connects, add them to this group. And there'll be some examples later, but that's kind of the basic idea. And then again, some hard problems, sessions. Like one of the things that I do by having the fact like When you send messages into channels from WebSockets, they go into this pool, and then one of the servers plucks them out and runs them, which means that if I send three messages in, different servers might pluck them out and run them at different times

23:08

Speaker 4: Like I might send one message to set my username that goes to server A, and another one to send a message that goes to server B And so one thing channel provides is a session framework for channels. So like sessions for HTTP, you can have sessions on a WebSocket that says, okay, set this WebSocket's username to this. And then when another thing runs it, it can read from the session. And even though it is on a different machine, via the Django sessions framework, it can still read that data across. And this also comes with utilities for enforcing serialization ordering. So if you say send two messages, you can tell channels make sure that this one has finished running first before you run the other one. Otherwise I run in parallel, which is faster, but sometimes can cause some very interesting race conditions.

23:54

Speaker 4: It has authentication. If you want, you You set one thing, like one line on either a consumer or a class, and it automatically takes the cookies from the HTTP request from the WebSocket connection, turns it into a Django user, and stores it on the session on the channel. And so WebSockets just come with users. They're the same users as your rest of your code users. You can do the same stuff on them. You can ask for permission checks. It's all the same. And so By default, you get good authentication and permission checking on WebSockets, which is something a lot of frameworks don't provide. Then finally there's some handy helpers around Things like I said, run server in when channels is installed just runs a run server that handles WebSockets. Like run server is suddenly this thing that runs things in parallel, handles WebSockets, is fully flexible.

24:42

Speaker 4: There is a run worker command that wraps running a process that runs commands for And a lot of this stuff is covered in much more detail in the rather extensive documentation I spent a lot of time writing. So if you're interested, that's all there. But let's try and have a bit more of a concrete example. Let's let's look at making a chat because it's the very easiest thing to show off, um, but it's the shortest one. So this is all you need to do a chat in channels. Um As I said, it's six lines. Uh you say when someone connects, you add them to a group called chat. When someone sends a message, you send a message to that group. And when someone disconnects, you remove them from the group. The last two lines are by the way optional, they're just make it more efficient. So really it's four lines technically, but yeah, I like some

25:28

Speaker 4: nice clean code And so you write these lines in a consumers. py file, you hook them up in routing like this. That's it. When you launch one server, the chat will work. You have to write some JavaScript, but you know, that's easy. Never no problems with that. And in fact, you can just do WebSocket. connect. It's easy these days. So and that's that and that's the simplicity of a basic channel's chat example. And of course, it wouldn't be Django without an easy way to do that. So there's of course class-based consumers where you go, well, that pattern of connecting and joining a group and leaving a group is actually really common. And so you just inherit from WebSocket and say, well, connection groups is chat. And that means that this generic consumer will handle joining the group when you connect, leaving the group when you disconnect.

26:13

Speaker 4: And then you just define a receive method that says, well, when I receive some messages, just run this root send. And you know, that's now that's interestingly still six lines, but I'd say six more readable lines and more reusable lines. And then again with routing, the nice thing is because a class-based consumer, it knows what channels it wants, you just say root class, and the routing extracts the channels from the consumer. And so consumer tells the routing framework, hi, I listen on WebSocket Connect, WebSocket Receive, WebSocket Disconnect, and then routing goes, oh okay, and then just pulls it out for you. So it's simpler in that sense. And then, of course, if you'd like a full-worked example that has multiple multiple chat channels, fully written JavaScript, other things, authentication. This

26:59

Speaker 4: is a repo full of fully worked examples of this and two other things that you can go and look at and play around with. It has documentation, lots of comments, and it even has README's with fun tasks to try of like challenges for you to you do. So if you're that kind of programmer is a really good way of doing that stuff. Now, normally my next example is a live blog, but I was bored last week, so it's not that anymore. Something people have been asking for a while in in channels is a more high level distraction. Going back to Meteor, one of the things Meteor did was, well, we had this really easy way to like You know, you move this slide around a page and it magically makes the other ones appear. That's great, right? Like channel, yeah. So um last week I wrote this. And so channels now has Full multiplexing, which means it has a built-in way of sending different streams down the same WebSocket, and it has data binding.

27:47

Speaker 4: And data binding is that thing where you move the sliders and the other stuff moves. So You uh what was this? It's about seven, eight lines, uh eight lines of code. You can do this now. You just go, okay, I have a data binding on my model. Um it sends down a stream, which is sort of a thing inside WebSocket called int val There's a permission check because security. Um so I have done I have done the very secure return true. Um you know Top notch, that'll pass any PCI audit. And so there's so check permission is for inbound. So like when somebody says, oh, my user changed the slider, it goes, okay, can I do this? Yes, true. And then group names as outbound. So it says, well, when I get a change, who do I send it to? Now here it's hard coded to just be a fixed string, but say you're doing a live blog.

28:34

Speaker 4: you would say, oh, send it to live blog. idflive blog. And that way people could connect to the s binding stream of the live blog they're on and the rest of it handled for you. And because I haven't done this in many years, let's do a live demo. Why not? It's a keynote. So here's one I prepared earlier. Ah, that works perfectly, see? I'm say prepared. And so this is that code you just saw with a few more comments because I'm nice running. And so, you know. You do the thing and the sliders move automatically. There is no extra code on the Django side other than the one I just showed you. The better part is that It's hooked into signals, so any change you make, I can just go in here and say, oh, this is now one. And as soon as I hit save, that slider moves in the background.

29:20

Speaker 4: Um it's the simplest demo I can do because live demos are very risky, but that's the simplest that's the sort of stuff you get from this, right? Like you can just have a faggy. And so that that's I think the the power of channels. And like one of the things I always struggled with was where should channels sit on that spectrum. And like for a long time I was like, well, really somebody else should write this package because it's a bit too opinionated for Django. And then I realized, well Django 's job as a framework is to be a bit opinionated and to have a common pattern. And so having this in there lets you do that. And I will point this out. The binding framework is fully subclassable. It is not just WebSockets. You can do whatever you like with it. But having a nice common one in there, I think, will really help Django. Like, you know, you can go away today and write a live blog in about 10 lines of code that just uses this framework.

30:09

Speaker 4: framework. And moreover, I think about next week I will have a basic JavaScript side wrapper that sort of wraps some of these operations for you. But it's very Framework agnostic. I do not wish to pick a side in the JavaScript wars. But it lets you do things like has the stream separation for you and does reconnection for you. And that that'd be part of it as well. And so again, that thing you just saw is in the examples repo with more comments and channel um challenges and things for you to do as well. Um they didn't even show you is that you can change the names live and those change too, it's fine And so this is kind of, you know, I pushed it out this morning because I thought I should have something to show off here. It's pretty stable, but it's not entirely finished yet. The JavaScript stuff is still to come. Uh but it's just kind of like one of the last things before channels 1.

30:55

Speaker 4: 0 is like getting this kind of stuff in place. And hopefully when we when we land there we're gonna have good support for like things you want you want to do, like WebSockets and data binding and that kind of stuff, but also good low-level support for other protocols. Let's look at that in a little bit more detail. And so I mentioned at the start of the presentation that I was like, oh, you can do email and chats and all these kind of things, MQTT. What does that mean? Well, if we look at the way channels work, as I said before, it makes things into single events. Like channels is an invented system. That's all it does. And so for WebSockets, it says, well, the events are connect, receive, disconnect, and send. Pretty simple. And you can do the same for other protocols, like emails where you send them and receive them. So we could have a server, a bit like Daphne, but for email.

31:44

Speaker 4: When someone sends you an email, puts a message onto a channel called email. receive. And then we have another channel called email. send where you put messages and they get delivered. And then suddenly you have the same thing I just showed you for WebSockets, but for email. You can have routing on email, you can root by domain, you can root by subject, you can do all of this stuff, you can have consumers on email. And suddenly you have inside Django an email answering system that can live in apps you know, next to your other code if you want to, you don't have to, of course. You have something like Slack. Slack has messages. You receive them, you send them. You have things like join and receive join and leave notifications. You can do that kind of systemally as well. MQTT, which is a very simplistic um publish subscribe protocol for Internet of Things. Again, like you can implement a server that sits there on the boundary of your Django

32:32

Speaker 4: installation and translates these external protocols into something that understands a channel's format internally. And what this is kind of doing is channels is not this generic messaging task solution. It is very much a coordination layer for doing messaging between servers, like for writing complex systems in the age of the modern web that work across a cluster, that let you write code in a sensible way, that won't deadlock all that kind of stuff. And it applies beyond just uh WebSockets. And so the process of doing something like this is not too difficult. And so if you are interested in doing one of these things, please first of all email me. I'll happily give you twenty pages of advice probably I can type very fast. But essentially what you do is while your Django code is still synchronous, the idea is that channels is the binding glue between asynchronous and

33:23

Speaker 4: So you can write a separate and asynchronous process that handles the protocol in question. Let's say we're doing, I don't know, Slack. It has a nice async IO-based process that uses lots of long-lived connections and listens to the API and stuff like that. And then using channels, you just tie in this implementation with dot send and dot receive many on the channel layer to the rest of your service. And all of the coordination and scheduling is done for you by channels And so you literally just say, well, you make a server, you make sure it takes a channel layer object that has these send and receive methods on it. There's a standard for that as I said before, and then you just make sure it sends to and from it. And then With that kind of effort you suddenly have this almost networking like protocol that you can just use to coordinate between processes.

34:08

Speaker 4: Like if you choose multi-processing in Python, it's like that, but without the sort of spawning element in a way And then once you've written this server, and presumably you have, I would hope, at least have some kind of scrawled spec on the back of a napkin of what the messages mean, I recommend at least something. You can then take that and then implement consumers against it. So if you said, okay, I have a Slack server and that sends slack. receive, Slack. joined. You can then run consumers on the channels, slap on receive, and say, okay, well, receive messages always have a username, they always have a body text. And like you You have the specification that you understand on both sides, and then you can write consumers against it. This could be different teams if you want to too. Specifications are a great way of establishing team boundaries in in companies

34:54

Speaker 4: And one of the other things, if you have sort of very different channels, so like, you know, by default, Django under channels runs in a big blob of stuff where everything handles everything. You can actually say for individual servers, well this is the beefy server, just run these channels. This is the web server, don't run the long-lived channels. And there's options on those things to sort of make the channels run on different servers if you want to. And so if you look at the bigger picture of all of this, you end up with sort of like this exploding diagram of wonderfulness where like at the center sits ASGI and we have HTTP. We have Django workers, but also other protocols to be part of the same system. You can have a scheduler sending in events. You can have custom demons that do like thumbnailing for you and stuff like this.

35:39

Speaker 4: And what this really I'm going for here is You know, in the age of the modern web, services and back-end services are no longer just a simple it's HTTP request response. If they are, congratulations, you have a nice site. I love the idea of this. Some of my best projects are ones that are like just static sites. But increasingly, bigger and more complex sites have a more complex architecture. And one of the things I'm trying to do with channels is push a standardized solution For perfectionists with deadlines, the idea of bringing Django's promise forwards into the future of well, how do we solve these new problems in a way that is standardized and flexible and that will get out of your way when you need it to And it's very much a tool for you to use. Nothing in channels ships entirely finished. It's kind of on purpose.

36:25

Speaker 4: The idea is that it's very easy to just take it and finish off the direction you want, but it's it's the 80% step. It's 80% of all the work, it's all the hard work done for you, and then you can build on top of it, and then if as you grow, if you want, you can increasingly remove parts of it and put your own versions in And there's more to be done. Channels is going to hit 1. 0 pretty soon. The application to be an official Django project is on the way. Now I've got the dep in for what official project means. first of all. Um and we also have some funding. Like if you're interested in working on channels, please come and talk to me here or at the Sprint. Um we have, as said Craig said at the start, we have some funding from Mozilla for channels. A very nice number, $150,000. And it's in marked just for channels. And so if you want to help maintain it or write new servers

37:12

Speaker 4: or do anything under this umbrella, we have funding to help give you for things like that as well. And then I want to finally end on a bit of a look forward. So if you think of the history of the web, in 1995 You're probably a desktop application, right? The web is a very new thing. There's like a sort of terrible McDonald's site with gifts. It's about all the web is at this point. In 2005, we are sort of in the dot-com age of like your website. You have nice rounded corners and gradients. It's really lovely. And this is where Django is born, right? Django is born in 2005. In 2015, we've moved on. We're now rich JavaScript websites, with now mobile apps talking to API backends. And then in another 10 years, who knows where we'll be? Like it's this open question. Like at the current rate of progress, things get more complicated, like websites getting more complicated.

38:01

Speaker 4: And part of the question I always ask the channels is what are the goals of a framework? Like does Django adapt and move on with the times? Do we adapt to what a changing web means? Or do we stay and do a good job of what we already do? And I'm sure you can guess my opinion. Um I am very much of the opinion that like we should keep advancing Django and keep that promise of A framework for perfectionistic deadlines for the modern web, not just not just for the the old web and the previous web. Thank you very much

38:52

Speaker 3: So we have some time for questions.

38:54

Speaker 4: We do.

38:54

Speaker 3: If you'd like to come to the mics at the front either side at the front of the room.

39:05

Speaker 4: Yeah, that's what happens. Um this is the thing, right? Like Django is built around relational databases, and so Unfortunately, we don't have the advantage of another data store we can use. Now if you had another store that used the RRM abstraction, which we have the specification for now, it would also work against that, like the admin does. But in that example, yes, Postgres was getting right, right, right, right, right, right, right, right. Um but in in the general case, like the number of applications where you do That kind of slider interface, that's a that that's a good demo, right? It's usually like a live blog or a chat and like you do want a backing store in that case. So yeah, you're right

39:45

Speaker 5: Do you see Asge turning into a PEP?

39:48

Speaker 4: Uh it is written in the PEP style. I cannot confirm or deny that was the intention. Uh I would like to eventually so like I believe that in in proof for proof for implementation first. Um so like when I'm happy with it and it's working well, I will then take it to the rest and also on I I need to I've talked to some from FLAS people initially, but I reached out to other frameworks in in in Python and say like, well, does this work for all of us? Can we agree on this as a step forward? I would love it to be a Python-wide thing as well.

40:20

Speaker 6: Hi, I'm not sure I understood all the features, but my question is, this is an event system. Does this provide what for me is the holy grail of um guaranteed delivery and higher of a high availability system?

40:38

Speaker 4: So when you have a you have you have two options. You have at most once or at least once. Now channels is at most once and that it is When it fails, it does not deliver. The other option is when it fails, it delivers twice. There is no holy grail in the middle, unfortunately. In all my testing, it always received 100% reliability because I'm not sitting there pulling cables out It generally is very reliable, but I as an engineer I must say that when it f it will fail with non-delivery. That's the design.

41:07

Speaker 6: And I guess the other thing I saw persistent sessions. So I was thinking, oh, if I if I close my browser and I come back, I'm going to get the message.

41:16

Speaker 4: No, so that that that that's persistent on a on a socket. So the idea is that a socket is a continuous thing, but the serving system is a set of separate servers, and so it makes sure that the socket as a single entity still has a persistent layer at the other end. But yeah, if you want sessions in that sense are HTTP cookies, right? And like you you cannot set those from sockets unfortunately. But what you can do is do things using JavaScript and local storage, but that gets into the more complex end of things. No problem.

41:44

Speaker 1: Do you have any big features planned for the uh 1. 11 integration?

41:48

Speaker 4: Not yet. Um all the features that I am planning are just in the external version. Um and also 1. 11 is still a target. We're not even released 1. 10 yet, so we'll see. Um but the idea is only like channels will be will hit 1. 0 as an external project and we'll continue as that for at least a while. Like whether it rolls in this core or not ever is a question. In many ways, Django Core has wanted to roll things out of core for a few years. And so this may prove the first example of that. What we will do though is stop mentioning it more in the official documentation and that kind of stuff. Making it obvious that it exists, but not making it perhaps fully cool. We'll see. It really depends. Thank you.

42:31

Speaker 3: I'm I'm really impressed. Thank you. Good stuff. Can you say something about uh how you load tested?

42:40

Speaker 4: Yes. So um oh well so um we have somebody doing running load tests at the moment um like on AWS with locusts and stuff like that. Um so generally so HTTP load testing there are tools for this WebSocket testing, there are far less tools for this. I have written a custom WebSocket testing tool, but there are other ones too. But generally like basically just setting up the system. Having custom consumers like, you know, do things like, oh, take the number and echo it back, and testing like, is the ordering correct? Is the content not corrupted and that kind of stuff? Right. And so far the load tests have shown performance on par with unicorn and a hundred percent reliability. So those are going well. But we're still in the early stages of doing load testing. So I want to be more confident before I come out with like a big role.

43:28

Speaker 7: You had uh mentioned early on about authentication, having the authentication layer. Is that a shared authentication with Django such that if you authenticate to Django, then your WebSocket connection is authenticated?

43:41

Speaker 4: That is correct. Yes. Um so in particular, um when WebSockets open, they get to send cookie headers as part of the negotiation process. They they they start at HTTP and send headers and cookies and then convert into binary halfway through So if you want to and we can we grab the cookies and then extract the authentication from the cookies and make it just whatever your logs in has on the site. So if you want to have it, it carries through. If you don't, you can have a separate login via tokens for sockets too if you want

44:11

Speaker 3: Well there's no more questions. Uh thank you very much for the questions, and let's thank Andrew again.

Questions this talk answers

Why is scaling WebSockets across multiple servers difficult?

WebSocket connections are long-lived and stateful, so they cannot be freely moved between servers. Broadcasting also requires servers to coordinate without saturating the network or creating a single point of failure.

Discussed at 9:51

What problem is Django Channels designed to solve?

Channels is a coordination foundation for running messaging-based services across a cluster, especially solving the problem of delivering events between servers. WebSockets are one use case, but the same layer also supports task offloading, chat, email, and other protocols.

Discussed at 10:37

How does the Channels channel layer route messages between servers and groups?

The channel layer lets applications send to and receive from channels, add or remove channels from groups, and broadcast to a group. It handles routing a message across the network, expanding a group into its member channels, and delivering the result to the appropriate clients.

Discussed at 14:37

How do Daphne, Django workers, and the channel layer fit together?

Daphne terminates HTTP and WebSocket connections and turns them into channel messages, while Django workers read those messages and execute the application code. The two parts can be scaled independently depending on whether the workload is connection-heavy or computation-heavy.

Discussed at 19:15

What does Django Channels add on top of the low-level channel layer?

The Django integration provides routing, consumers, sessions, authentication, permission checks, message-ordering utilities, and commands for running servers and workers. Consumers handle messages in a view-like way, while Channels manages the surrounding event loop and infrastructure.

Discussed at 20:35

How do you build a basic chat application with Django Channels?

On connection, add the client to a chat group; when a message arrives, send it to that group; and on disconnection, remove the client. The talk presents this as a minimal four-line implementation, or six lines with efficiency-related cleanup.

Discussed at 24:42

Can Django Channels handle protocols other than WebSockets?

Yes. A protocol adapter can translate events from email, Slack-like messaging, or MQTT into the Channels format, after which Django routing and consumers can process them. The protocol-specific process remains separate while Channels handles coordination and scheduling between processes.

Discussed at 30:55

Presenters

Note: We understand that names change, people change, and bodies change. We respect each individual's journey and privacy. If you have any concerns about a video or need us to remove content, please don't hesitate to contact us. We will handle your request with care and promptly address any issues.

More videos by Andrew Godwin

More videos from DjangoCon US