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 在通道層上調用方法時不再需要它。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM