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.

Avatar for Andrew Godwin

Andrew Godwin

March 09, 2016
Tweet

More Decks by Andrew Godwin

Other Decks in Programming

Transcript

  1. 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?
  2. There are standard channel names: STANDARDS Clients have unique response

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

    WebSocket close - HTTP response chunk But you can make your own channel names and formats too.
  4. Channels are network-transparent, and protocol termination is separate from business

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

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

    2 protocol server worker server channels client socket worker server thread 3 process 1
  7. 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.
  8. EXAMPLE This echoes messages back: def ws_message(message): text = message.content['text']

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

    for client in client_list: Channel(client).send({ 'text': message.content['id'], })
  10. 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'], })
  11. EXAMPLE And you can send to channels from anywhere: class

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

    Channel('thumbnail').send({ 'id': str(image.id), }) return render(...)
  13. 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', }
  14. EXAMPLE The HTTP channel just goes to the view system

    by default. routing = { # This is actually the default 'http.request': 'channels.asgi.ViewConsumer', }
  15. 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, } )