Django channels
Transcript of 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.
Andrew Godwin
Concept of Channels
What is Channel
• First-in first-out queue
• At-Most-Once delivery
How to use 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
Three separate layers
• Interface servers
• The channel backend
• The Workers
def my_consumer(message): Channel( message.reply_channel ).send(HttpResponse(“ok”))
channel_routing = { "http.request": "myapp.consumers.my_consumer", }
Groups
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)
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)
Let’s build a simple chat room
pip install channels
INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'channels', )
Add ‘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
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
pip install autobahn[twisted] python manage.py runwsserver
Run WebSocket server
socket = new WebSocket("ws://127.0.0.1:9000/");
socket.onmessage = function(e) { $('body').append(e.data+'<br>'); }
Added Some javascript
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 )
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.
@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 )
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)
DEMO
https://github.com/daikeren/channel-example
Some Thoughts
• Channels will become a built-in feature for Django 1.10
• Not stable yet
• Documentation…
• Read the source code!
Q&A