Django and this thing called Channels

Django and this thing called Channels

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

077e9a0cb34fa3eba2699240c9509717?s=128

Andrew Godwin

March 09, 2016
Tweet

Transcript

  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

    dispatch
  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