Django channels

27
Django Channels Andy Dai [email protected]

Transcript of Django channels

Page 2: Django channels

Django awarded MOSS Grant

We've been awarded $150,000 to help fund the development of Channels, the initiative to rewrite the

core of Django to support (among other things) WebSockets and background tasks, and to integrate

key parts of the Django REST Framework request/response code into Django, such as content

negotiation.

Page 3: Django channels

Andrew Godwin

Page 4: Django channels

Concept of Channels

Page 5: Django channels

What is Channel

• First-in first-out queue

• At-Most-Once delivery

Page 6: Django channels

How to use Channels?

Page 7: Django channels

def my_consumer(message): pass

channel_routing = { "some-channel": "myapp.consumers.my_consumer", }

Consumer function

Assign a channel to it in the channel routing

Page 8: Django channels

Three separate layers

• Interface servers

• The channel backend

• The Workers

Page 9: Django channels

def my_consumer(message): Channel( message.reply_channel ).send(HttpResponse(“ok”))

channel_routing = { "http.request": "myapp.consumers.my_consumer", }

Page 10: Django channels

Groups

Page 11: Django channels

Channels can’t do broadcast

redis_conn = redis.Redis("localhost", 6379)

@receiver(post_save, sender=BlogUpdate) def send_update(sender, instance, **kwargs): # Loop through all response channels and send the update for reply_channel in redis_conn.smembers("readers"): Channel(reply_channel).send( id=instance.id, content=instance.content, )

# Connected to websocket.connect def ws_connect(message): # Add to reader set redis_conn.sadd("readers", message.reply_channel.name)

Page 12: Django channels

With Groups

@receiver(post_save, sender=BlogUpdate) def send_update(sender, instance, **kwargs): Group("readers").send( id=instance.id, content=instance.content, )

# Connected to websocket.connect and websocket.keepalive def ws_connect(message): # Add to reader group Group("readers").add(message.reply_channel)

Page 13: Django channels

Let’s build a simple chat room

Page 14: Django channels

pip install channels

Page 15: Django channels

INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'channels', )

Add ‘channels’

Page 16: Django channels

CHANNEL_BACKENDS = { "default": { "BACKEND": "channels.backends.redis_py.RedisChannelBackend", "ROUTING": { "websocket.connect": "chatroom.consumers.ws_add", "websocket.keepalive": "chatroom.consumers.ws_add", "websocket.receive": "chatroom.consumers.ws_message", "websocket.disconnect": "chatroom.consumers.ws_disconnect", }, }, }

CHANNEL_BACKENDS

16

Page 17: Django channels

from channels import Group

# Connected to websocket.connect and websocket.keepalive def ws_add(message): Group("chat").add(message.reply_channel)

# Connected to websocket.receive def ws_message(message): Group("chat").send(message.content)

# Connected to websocket.disconnect def ws_disconnect(message): Group("chat").discard(message.reply_channel)

consumers.py

Page 18: Django channels

pip install autobahn[twisted] python manage.py runwsserver

Run WebSocket server

Page 19: Django channels

socket = new WebSocket("ws://127.0.0.1:9000/");

socket.onmessage = function(e) { $('body').append(e.data+'<br>'); }

Added Some javascript

Page 20: Django channels

Persisting Datafrom channels.decorators import channel_session

@channel_session def ws_add(message): room = message.content.get('path').strip('/') message.channel_session['room'] = room print "Room: {room} - ws_add".format(room=room) Group("chat-%s" % room).add( message.reply_channel )

@channel_session def ws_keepalive(message): room = message.channel_session['room'] Group("chat-%s" % room).add( message.reply_channel )

Page 21: Django channels

Linearization

The linearize decorator. Any handler decorated with this will use locking to ensure it does not run at the same time as

any other view with linearize on messages with the same reply channel.

Page 22: Django channels

@linearize @channel_session def ws_add(message): room = message.content.get('path').strip('/') message.channel_session['room'] = room print "Room: {room} - ws_add".format(room=room) Group("chat-%s" % room).add( message.reply_channel )

@linearize @channel_session def ws_message(message): Group("chat-%s" % message.channel_session['room']).send( content=message.content )

Page 23: Django channels

Authenticationfrom channels.auth import ( http_session_user, channel_session_user, transfer_user )

@channel_session @http_session_user def ws_add(message): # Copy user from HTTP to channel session transfer_user(message.http_session, message.channel_session) # Add them to the right group Group( "chat-%s" % message.user.username[0] ).add(message.reply_channel)

@channel_session_user def ws_keepalive(message): # Keep them in the right group Group( "chat-%s" % message.user.username[0] ).add(message.reply_channel)

Page 24: Django channels

DEMO

Page 25: Django channels

https://github.com/daikeren/channel-example

Page 26: Django channels

Some Thoughts

• Channels will become a built-in feature for Django 1.10

• Not stable yet

• Documentation…

• Read the source code!

Page 27: Django channels

Q&A