$30 off During Our Annual Pro Sale. View Details »

Channels (Under The Hood)

Channels (Under The Hood)

A talk I gave at Django Under The Hood 2016.

Andrew Godwin

November 03, 2016
Tweet

More Decks by Andrew Godwin

Other Decks in Programming

Transcript

  1. View Slide

  2. Andrew Godwin
    Hi, I'm
    Django core developer
    Senior Software Engineer at
    Used to complain about migrations a lot

    View Slide

  3. It's magic.

    View Slide

  4. It's magic.

    View Slide

  5. The Problem
    1

    View Slide

  6. The Web is changing.

    View Slide

  7. WebSockets

    View Slide

  8. WebSockets
    WebRTC
    Long-polling
    MQTT
    Server-Sent Events

    View Slide

  9. Python is synchronous.
    Django is synchronous.

    View Slide

  10. Synchronous code is easier to write.
    Single-process async is not enough.

    View Slide

  11. Proven design pattern
    Not too hard to reason about

    View Slide

  12. What could fit these constraints?

    View Slide

  13. Loose Coupling
    2

    View Slide

  14. Not too tied to WebSockets
    Not too tied to Django

    View Slide

  15. Well-defined, minimal interfaces

    View Slide

  16. Easy to swap out or rewrite

    View Slide

  17. The Message Bus
    HTTP
    Server
    Message Bus
    WSock
    Server
    Django
    Project

    View Slide

  18. What do you send?
    How do you send it?

    View Slide

  19. ASGI

    View Slide

  20. nonblocking send
    blocking receive
    add to group
    discard from group
    send to group

    View Slide

  21. JSON-compatible,
    dictionary-based
    messages onto
    named channels

    View Slide

  22. Concrete Ideas
    3

    View Slide

  23. Develop using concrete examples

    View Slide

  24. WebSocket
    connect
    receive
    disconnect
    accept/reject
    send

    View Slide

  25. WebSocket
    websocket.connect
    websocket.send!abc1234
    websocket.receive
    websocket.disconnect

    View Slide

  26. At-most-once
    First In First Out
    Backpressure via capacity
    Not sticky
    No guaranteed ordering
    No serial processing

    View Slide

  27. HTTP
    & WS
    Channel Layer
    Django
    Worker
    HTTP
    & WS
    Django
    Worker
    Django
    Worker
    Django
    Worker

    View Slide

  28. {
    "text": "Hello, world!",
    "path": "/chat/socket/",
    "reply_channel": "websocket.send!9m12in2p",
    }

    View Slide

  29. Developed and spec'd
    HTTP WebSocket
    Rough drafts
    IRC Email Slack
    Please, no.
    Minecraft Mainframe Terminal

    View Slide

  30. {
    "reply_channel": "http.response!g23vD2x5",
    "method": "GET",
    "http_version": "2",
    "path": "/chat/socket/",
    "query_string": "foo=bar",
    "headers": [["cookie", "abcdef..."]],
    }

    View Slide

  31. At-most-once
    First In First Out
    Backpressure via capacity
    Not sticky
    No guaranteed ordering
    No serial processing

    View Slide

  32. At-most-once
    First In First Out
    Backpressure via capacity
    Not sticky
    No guaranteed ordering
    No serial processing

    View Slide

  33. "order" key on receive messages
    Connection acceptance

    View Slide

  34. Daphne
    HTTP/WebSocket Server
    Channels
    Django integration
    asgi-redis
    Redis backend
    asgi-ipc
    Local memory backend
    asgiref
    Shared code and libs

    View Slide

  35. Django-ish
    4

    View Slide

  36. It can take several tries
    to get a nice API.

    View Slide

  37. Consumers based on Views
    Callable that takes an object
    Decorators for functionality
    Class-based generics

    View Slide

  38. @channel_session
    def chat_receive(message):
    name = message.channel_session["name"]
    message.reply_channel.send({"text": "OK"})
    Group("chat").send({
    "text": "%s: %s" % (name, message["text"]),
    })
    Message.objects.create(
    name=name,
    content=message["text"],
    )

    View Slide

  39. Routing based on URLs
    List of regex-based matches
    Includes with prefix stripping on paths
    More standardised interface

    View Slide

  40. routing = [
    route(
    "websocket.receive",
    consumers.chat_receive,
    path=r"^/chat/socket/$",
    ),
    include("stats.routing", path="^/stats/"),
    route_class(ConsumerClass, path="^/v1/"),
    ]

    View Slide

  41. Sessions are the only state
    Sessions hang off reply channels not cookies
    Uses same sessions backends
    Available on the consumer's argument
    Can also access long-term cookie sessions

    View Slide

  42. @enforce_ordering
    def receive_consumer(message):
    Log.objects.create(...)

    View Slide

  43. session = session_for_reply_channel(
    message.reply_channel.name
    )
    if not session.exists(session.session_key):
    try:
    session.save(must_create=True)
    except CreateError:
    # Session wasn't unique
    raise ConsumeLater()
    message.channel_session = session

    View Slide

  44. No Middleware
    New-style middleware half works
    No ability to capture sends
    Decorators replace most cases

    View Slide

  45. View/HTTP Django still there
    Can intermingle or just use one type
    View system is just a consumer now

    View Slide

  46. def view_consumer(message):
    replies = AsgiHandler()(message)
    for reply in replies:
    while True:
    try:
    message.reply_channel.send(reply)
    except ChannelFull:
    time.sleep(0.05)
    else:
    break

    View Slide

  47. Signals and commands
    runserver works as expected
    Signals for handling lifecycle
    staticfiles configured for development

    View Slide

  48. Beyond
    5

    View Slide

  49. Generalised async communication

    View Slide

  50. Service messaging
    Security/CPU separation
    Sync & Async / Py2 & Py3

    View Slide

  51. Diversity of implementations
    More web servers
    More channel layers

    View Slide

  52. More optimisation
    More efficient bulk sends
    Less network traffic on receive

    View Slide

  53. More maintainers
    More viewpoints, more time

    View Slide

  54. 1.0 coming soon
    Stable APIs for everything except binding

    View Slide

  55. Thanks.
    Andrew Godwin
    @andrewgodwin
    channels.readthedocs.io
    github.com/andrewgodwin/channels-examples

    View Slide