Introduction to Django Channels - ConFoo Vancouver 2016

A985c35d6be3c88a87d92b92b0d3756f?s=47 ericholscher
December 07, 2016

Introduction to Django Channels - ConFoo Vancouver 2016

Django has been the largest web framework in Python for more than 7 years. However, web development hasn't stood still, with tools like Node.js and Go taking a more asynchronous approach to web development.

Channels is Django's answer to this, giving Django an answer to other async web frameworks, allowing native support for WebSockets and more. This talk will be an introduction to the architecture and implementation of Django Channels.

A985c35d6be3c88a87d92b92b0d3756f?s=128

ericholscher

December 07, 2016
Tweet

Transcript

  1. 1.

    Eric Holscher ConFoo Vancouver December 7, 2016 Python & Django

    Go Async An introduction to Django Channels
  2. 2.

    Who am I • Co-Founder of Read the Docs •

    Co-Founder of Write the Docs • Longtime member of the Django community
  3. 3.

    Today • Understand the architecture of Channels • Understand the

    tradeoffs required • Know if it fits your use case and organization • Know how to get started
  4. 7.

    Channels adds ability for Django to handle H P/2, websockets,

    and other things outside a normal request/response cycle
  5. 8.
  6. 10.
  7. 11.

    Current Status • 1.0 imminent • Lives outside of Django

    itself • An official project of the Django team • Will perhaps merge into core for Django 2.0
  8. 14.

    Interface Server • Holds connections open to the world •

    Speaks both H P and Websocket • Passes messages along the channels, doesn’t actually process anything
  9. 15.

    Channels • First In First Out • At most once

    delivery • No guaranteed ordering • Messages expire a er a set time • ‘websocket.connect’, ‘http.request’, ‘chat.message’
  10. 16.

    Messages • Send across the channels • Can have arbitrary

    content based on differing protocols • Usually JSON
  11. 17.

    # Sent on a ‘http.request’ channel { 'reply_channel': 'http.response!g23vD2x5', 'method':

    'GET', 'http_version': '2', 'path': '/chat/socket/', 'query_string': 'foo=bar', 'headers': [['cookie', 'abcdef...']], } Messages
  12. 18.

    Routing • Similar to Django URLConf • Allows for matching

    arbitrary things on the incoming messages • Can match against `path` on H P requests, or `address` on email sending
  13. 19.

    routing = [ route( "websocket.receive", consumers.chat_receive, path=r"^/chat/socket/$", ), route( "email.receive",

    comment_response, address=r".*@example.com$" ) include("stats.routing", path="^/stats/"), ] Routing
  14. 20.

    Consumer • Similar to a Django View • Takes a

    `message` and… does something
  15. 22.

    Groups • Contain sets of channels • Only allow add/remove/send

    — because distributed systems • Useful for keeping state and sending messages across sets of channels
  16. 25.

    # Routing channel_routing = { 'http.request': consumers.http_consumer } # Message

    { 'reply_channel': 'http.response!g23vD2x5', 'method': 'GET', 'http_version': '2', 'path': '/chat/socket/', 'query_string': 'foo=bar', 'headers': [['cookie', 'abcdef...']], } # Consumer def http_consumer(message): response = HttpResponse('Hey there') message.reply_channel.send(response.channel_encode()) HTTP Request
  17. 26.

    # Routing channel_routing = { 'websocket.connect': consumers.ws_connect, 'websocket.receive': consumers.ws_receive, 'websocket.disconnect':

    consumers.ws_disconnect, } # Message { 'text': 'Hello, world!', 'path': '/chat/socket/', 'reply_channel': 'websocket.send!9m12in2p', } # Consumer def ws_connect(message): message.reply_channel.send(message['text']) Websocket Request
  18. 28.

    Authentication & Sessions • Slightly tricky • ‘websocket.connect’ has H

    P cookies & auth • There are two sessions, “http_sessions” & “channel_sessions”
  19. 29.

    @channel_session_user_from_http def ws_add(message): Group( "chat-%s" % message.user.username[0] ).add(message.reply_channel) @channel_session_user def

    ws_message(message): Group("chat-%s" % message.user.username[0]).send({ "text": message['text'], }) Authentication & Sessions
  20. 30.

    Generic Consumers • Similar to Generic Views • Abstracts common

    use cases for simpler logic • Ships with Websocket & JSON generic consumers currently
  21. 31.

    class MyConsumer(JsonWebsocketConsumer): def connection_groups(self, **kwargs): return ["test"] def receive(self, content,

    **kwargs): """ Called when a message is received with decoded JSON content """ # Simple echo self.send(content) Generic Consumers
  22. 32.

    MultiPlexing • Allows you to send multiple data types over

    a single connection • Uses ‘stream’ and ‘payload’ keys • Sends the payload to the mapped stream
  23. 34.

    Data Binding • Lets you keep client & server data

    models in sync • Update each side on change to either side in realtime • Uses Django serialization and multiplexing as standard wire format
  24. 35.

    # Routing channel_routing = [ route_class(Demultiplexer, path="^/binding/"), route("binding.intval", IntegerValueBinding.consumer), ]

    # Consumer class IntegerValueBinding(WebsocketBinding): model = IntegerValue stream = "intval" fields = ["name", "value"] def group_names(self, instance, action): return ["intval-updates"] def has_permission(self, user, action, pk): return True Data Binding
  25. 36.

    # Message (Client) { "payload": { "action": "update", "data": {

    "value": "56" }, "pk": "2" }, "stream": "intval" } # Message (Server) { "payload": { "action": "update", "data": { "name": "Cool", "value": 56 }, "model": "values.integervalue", "pk": 2 }, "stream": "intval" } Data Binding
  26. 38.
  27. 39.

    # settings.py CHANNEL_LAYERS = { "default": { "BACKEND": "asgi_redis.RedisChannelLayer", "CONFIG":

    { "hosts": [("localhost", 6379)], }, "ROUTING": "myproject.routing.channel_routing", }, } # asgi.py import os from channels.asgi import get_channel_layer os.environ.setdefault("DJANGO_SETTINGS_MODULE", "my_project.settings") channel_layer = get_channel_layer() Configuration
  28. 41.

    # Run a Channel Layer redis-server # Run an Interface

    Server daphne my_project.asgi:channel_layer # Run the Workers ./manage.py runworker All services
  29. 43.

    Can limit the channels workers listen on, to split up

    long running and short running processes
  30. 45.
  31. 48.
  32. 49.