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

Introduction to Django Channels - ConFoo Vancou...

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.

ericholscher

December 07, 2016
Tweet

More Decks by ericholscher

Other Decks in Technology

Transcript

  1. Eric Holscher ConFoo Vancouver December 7, 2016 Python & Django

    Go Async An introduction to Django Channels
  2. Who am I • Co-Founder of Read the Docs •

    Co-Founder of Write the Docs • Longtime member of the Django community
  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. Channels adds ability for Django to handle H P/2, websockets,

    and other things outside a normal request/response cycle
  5. 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
  6. Interface Server • Holds connections open to the world •

    Speaks both H P and Websocket • Passes messages along the channels, doesn’t actually process anything
  7. 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’
  8. Messages • Send across the channels • Can have arbitrary

    content based on differing protocols • Usually JSON
  9. # 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
  10. 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
  11. 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
  12. Consumer • Similar to a Django View • Takes a

    `message` and… does something
  13. Groups • Contain sets of channels • Only allow add/remove/send

    — because distributed systems • Useful for keeping state and sending messages across sets of channels
  14. # 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
  15. # 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
  16. Authentication & Sessions • Slightly tricky • ‘websocket.connect’ has H

    P cookies & auth • There are two sessions, “http_sessions” & “channel_sessions”
  17. @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
  18. Generic Consumers • Similar to Generic Views • Abstracts common

    use cases for simpler logic • Ships with Websocket & JSON generic consumers currently
  19. 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
  20. MultiPlexing • Allows you to send multiple data types over

    a single connection • Uses ‘stream’ and ‘payload’ keys • Sends the payload to the mapped stream
  21. 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
  22. # 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
  23. # 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
  24. # 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
  25. # Run a Channel Layer redis-server # Run an Interface

    Server daphne my_project.asgi:channel_layer # Run the Workers ./manage.py runworker All services
  26. Can limit the channels workers listen on, to split up

    long running and short running processes