Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Django and this thing called Channels

Django and this thing called Channels

A talk I gave at Python SF Meetup, March 2016.

Andrew Godwin

March 09, 2016

More Decks by Andrew Godwin

Other Decks in Programming


  1. Andrew Godwin @andrewgodwin DJANGO and this thing called CHANNELS

  2. Andrew Godwin Hi, I'm Django core developer Senior Software Engineer

    at Likes complaining about networking
  3. The Problem First: The Solution Then: The Future Finally:

  4. The Problem WSGI ain't so bad

  5. Browser HTTP Webserver Django WSGI View Handler

  6. Browser WebSocket Webserver Django ???? ??? ????

  7. Very hard to deadlock/cause errors Seems "Django-ish" DESIGN GOALS Scales

    decently All works inside runserver
  8. Browser WebSocket Webserver Django Channels Consumers Routing

  9. Browser HTTP Webserver Django Channels Consumer Ch. Routing View URL

  10. The Solution A Series Of Channels

  11. An ordered, first-in-first-out, at-most once queue with expiry and delivery

    to only one listening consumer. Identified by a unique unicode name. WHAT IS A CHANNEL?
  12. Producers Consumers Channel

  13. There are standard channel names: STANDARDS Clients have unique response

    channels: http.request websocket.connect websocket.receive !http.response.jaS4kSDj !websocket.send.Qwek120d
  14. There are standard message formats: STANDARDS - HTTP request -

    WebSocket close - HTTP response chunk But you can make your own channel names and formats too.
  15. But how do we do this without making Django asynchronous?

  16. Channels are network-transparent, and protocol termination is separate from business

    logic process 1 process 2 protocol server worker server channels client socket
  17. Async/cooperative code, but none of yours process 1 process 2

    protocol server worker server channels client socket Your logic running synchronously worker server
  18. For runserver, we run them as threads thread 1 thread

    2 protocol server worker server channels client socket worker server thread 3 process 1
  19. A view receives a request and sends a single response.

    WHAT IS A CONSUMER? A consumer receives a message and sends zero or more messages.
  20. EXAMPLE This echoes messages back: def ws_message(message): text = message.content['text']

    reply = "You said: %s" % text message.reply_channel.send({ 'text': reply, })
  21. EXAMPLE This notifies clients of new blog posts: def newpost(message):

    for client in client_list: Channel(client).send({ 'text': message.content['id'], })
  22. EXAMPLE But Channels has a better solution for that: #

    Channel "websocket.connect" def connect(message): Group('liveblog').add( message.reply_channel) # Channel "new-liveblog" def newpost(message): Group('liveblog').send({ 'text': message.content['id'], })
  23. EXAMPLE And you can send to channels from anywhere: class

    LiveblogPost(models.Model): ... def save(self): ... Channel('new-liveblog').send({ 'id': str(self.id), })
  24. EXAMPLE You can offload processing from views: def upload_image(request): ...

    Channel('thumbnail').send({ 'id': str(image.id), }) return render(...)
  25. EXAMPLE All the routing is set up like URLs: from

    .consumers import ws_connect routing = { 'websocket.connect': ws_connect, 'websocket.receive': 'a.b.receive', 'thumbnail': 'images.consumers.thumb', }
  26. EXAMPLE The HTTP channel just goes to the view system

    by default. routing = { # This is actually the default 'http.request': 'channels.asgi.ViewConsumer', }
  27. The Future What does it all mean?

  28. WSGI

  29. WSGI 2?

  30. ASGI channels.readthedocs.org/en/latest/asgi.html

  31. channel_layer.send(channel, message) channel_layer.receive_many(channels) + message standards + extensions (groups, stats)

  32. Daphne HTTP/WebSocket ASGI server asgiref Memory layer, conformance suite, WSGI

    adapters asgi_redis Redis-based channel layer
  33. Raw ASGI app example: while True: channel, message = layer.receive_many(

    ["http.request"], block = True, ) if channel is None: continue layer.send( message['reply_channel'], { 'content': 'Hello World!', 'status': 200, } )
  34. Django 1.10

  35. Django 1.10 And 1.8, 1.9 as a third-party app

  36. It's all optional. No need to change anything if you

    don't want to use channels.
  37. pip install channels channels.readthedocs.org

  38. Thanks. Andrew Godwin @andrewgodwin