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
Tweet

More Decks by Andrew Godwin

Other Decks in Programming

Transcript

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

    View Slide

  2. Andrew Godwin
    Hi, I'm
    Django core developer
    Senior Software Engineer at
    Likes complaining about networking

    View Slide

  3. The Problem
    First:
    The Solution
    Then:
    The Future
    Finally:

    View Slide

  4. The Problem
    WSGI ain't so bad

    View Slide

  5. Browser
    HTTP
    Webserver
    Django
    WSGI
    View
    Handler

    View Slide

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

    View Slide

  7. Very hard to deadlock/cause errors
    Seems "Django-ish"
    DESIGN GOALS
    Scales decently
    All works inside runserver

    View Slide

  8. Browser
    WebSocket
    Webserver
    Django
    Channels
    Consumers
    Routing

    View Slide

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

    View Slide

  10. The Solution
    A Series Of Channels

    View Slide

  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?

    View Slide

  12. Producers
    Consumers
    Channel

    View Slide

  13. There are standard channel names:
    STANDARDS
    Clients have unique response channels:
    http.request
    websocket.connect
    websocket.receive
    !http.response.jaS4kSDj
    !websocket.send.Qwek120d

    View Slide

  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.

    View Slide

  15. But how do we do this without making
    Django asynchronous?

    View Slide

  16. Channels are network-transparent,
    and protocol termination is separate
    from business logic
    process 1 process 2
    protocol
    server
    worker
    server
    channels
    client
    socket

    View Slide

  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

    View Slide

  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

    View Slide

  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.

    View Slide

  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,
    })

    View Slide

  21. EXAMPLE
    This notifies clients of new blog posts:
    def newpost(message):
    for client in client_list:
    Channel(client).send({
    'text': message.content['id'],
    })

    View Slide

  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'],
    })

    View Slide

  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),
    })

    View Slide

  24. EXAMPLE
    You can offload processing from views:
    def upload_image(request):
    ...
    Channel('thumbnail').send({
    'id': str(image.id),
    })
    return render(...)

    View Slide

  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',
    }

    View Slide

  26. EXAMPLE
    The HTTP channel just goes to the view system
    by default.
    routing = {
    # This is actually the default
    'http.request':
    'channels.asgi.ViewConsumer',
    }

    View Slide

  27. The Future
    What does it all mean?

    View Slide

  28. WSGI

    View Slide

  29. WSGI 2?

    View Slide

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

    View Slide

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

    View Slide

  32. Daphne HTTP/WebSocket ASGI server
    asgiref Memory layer, conformance suite,
    WSGI adapters
    asgi_redis Redis-based channel layer

    View Slide

  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,
    }
    )

    View Slide

  34. Django 1.10

    View Slide

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

    View Slide

  36. It's all optional.
    No need to change anything if you don't
    want to use channels.

    View Slide

  37. pip install channels
    channels.readthedocs.org

    View Slide

  38. Thanks.
    Andrew Godwin
    @andrewgodwin

    View Slide