django之websocket(基于redis服务器与Channels实现)

一、Channels介绍

Channels改变Django在下面和通过Django的同步核心编织异步代码,允许Django项目不仅处理HTTP,还需要处理需要长时间连接的协议 - WebSockets,MQTT,chatbots,amateur radio等等。

它在保留Django同步和易用性的同时实现了这一点,允许您选择编写代码的方式 - 以Django视图,完全异步或两者混合的方式同步。除此之外,它还提供了与Django的auth系统,会话系统等的集成,可以比以往更轻松地将仅HTTP项目扩展到其他协议。

它还将此事件驱动架构与通道层捆绑在一起,该系统允许在流程之间轻松通信,并将项目分成不同的流程。

Channels提供了编写基本消费者的工具- 可以处理聊天消息或通知的各个部分 - 并将它们与URL路由,协议检测和其他便利的东西联系在一起,以制作完整的应用程序。

传统的Django视图仍然存在于Channels并且仍然可用,它们被封装在一个名为的ASGI应用程序中(ASGI是构建Channels的异步服务器规范的名称。与WSGI一样,它旨在不​​同的服务器和框架之间进行选择,而不是被锁定在Channels和服务器Daphne中。

二、Channels的使用

1.集成Channels库

1)为Channels创建根路由配置(在项目文件中创建routing.py)

from channels.routing import ProtocolTypeRouter

application = ProtocolTypeRouter({
    # (http->django views is added by default)
})

2)将Channels注册到settings.py INSTALLED_APPS中

3)在根路由配置中指向Channels,settings.py中添加

ASGI_APPLICATION = 'object.routing.application'

Channels服务器的运行

python manage.py runserver
#如果运行成功,显示
#Starting ASGI/Channels development server at http://127.0.0.1:8000/

2.消费者的编写

当Django接受HTTP请求时,它会查询根URLconf以查找视图函数,然后调用视图函数来处理请求。类似地,当Channels接受WebSocket连接时,它会查询根路由配置以查找使用者,然后调用使用者的各种函数来处理来自连接的事件。

1)app中创建文件routing.py,编写路由处理websocket请求

from django.conf.urls import url

from . import consumers

websocket_urlpatterns = [
    url(r'^ws/chat/(?P<room_name>[^/]+)/$', consumers.ChatConsumer),
]

2)app中创建文件consumers.py,编写处理websocket脚本

from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer
import json

class ChatConsumer(WebsocketConsumer):
    def connect(self):
        self.room_name = self.scope['url_route']['kwargs']['room_name']
        self.room_group_name = 'chat_%s' % self.room_name

        # Join room group
        async_to_sync(self.channel_layer.group_add)(
            self.room_group_name,
            self.channel_name
        )

        self.accept()

    def disconnect(self, close_code):
        # Leave room group
        async_to_sync(self.channel_layer.group_discard)(
            self.room_group_name,
            self.channel_name
        )

    # Receive message from WebSocket
    def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json['message']

        # Send message to room group
        async_to_sync(self.channel_layer.group_send)(
            self.room_group_name,
            {
                'type': 'chat_message',
                'message': message
            }
        )

    # Receive message from room group
    def chat_message(self, event):
        message = event['message']

        # Send message to WebSocket
        self.send(text_data=json.dumps({
            'message': message
        }))
consumers.py

3)将app路由注册到根路由中

from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
import chat.routing

application = ProtocolTypeRouter({
    # (http->django views is added by default)
    'websocket': AuthMiddlewareStack(
        URLRouter(
            chat.routing.websocket_urlpatterns
        )
    ),
})

3.channel layer的启用

channel layer是一种通信系统。它允许多个消费者实例彼此交谈,以及与Django的其他部分交谈。

channel layer提供以下抽象:

  • channel是其中消息可以被发送到一个邮箱。每个channel都有一个名字。拥有channel名称的任何人都可以向channel发送消息。
  • group是一组相关的渠道。一个group有一个名字。具有group名称的任何人都可以按名称向group添加/删除频道,并向组中的所有频道发送消息。

使用一个使用Redis作为其后备存储的通道层。要在端口6379上启动Redis服务器

settings.py中添加

CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels_redis.core.RedisChannelLayer',
        'CONFIG': {
            "hosts": [('127.0.0.1', 6379)],
        },
    },
}

然后使用docker开启redis

docker run -p 6379:6379 -d redis:版本号

 4.消费者的异步

将消费者处理脚本重写为异步

from channels.generic.websocket import AsyncWebsocketConsumer
import json

class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.room_name = self.scope['url_route']['kwargs']['room_name']
        self.room_group_name = 'chat_%s' % self.room_name

        # Join room group
        await self.channel_layer.group_add(
            self.room_group_name,
            self.channel_name
        )

        await self.accept()

    async def disconnect(self, close_code):
        # Leave room group
        await self.channel_layer.group_discard(
            self.room_group_name,
            self.channel_name
        )

    # Receive message from WebSocket
    async def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json['message']

        # Send message to room group
        await self.channel_layer.group_send(
            self.room_group_name,
            {
                'type': 'chat_message',
                'message': message
            }
        )

    # Receive message from room group
    async def chat_message(self, event):
        message = event['message']

        # Send message to WebSocket
        await self.send(text_data=json.dumps({
            'message': message
        }))
consumers.py

异步使用者可以提供更高级别的性能,因为他们在处理请求时不需要创建其他线程。

这个新代码用于ChatConsumer与原始代码非常相似,但有以下区别:

  • ChatConsumer现在继承AsyncWebsocketConsumer而不是 WebsocketConsumer
  • 所有方法 由async def定义
  • await 用于调用执行I / O的异步函数。
  • async_to_sync 在通道层上调用方法时不再需要它。
posted @ 2019-05-27 09:33  爱学习的红领巾  阅读(2104)  评论(0编辑  收藏  举报