Django实现支持websocket协议

websocket的简单介绍可参考博主之前的博文websocket,本片通过测试示例代码介绍一下Django实现支持websocket协议

django默认wsgi不支持websocket协议,只支持http协议。而asgi(daphne)既支持http也支持websocket协议。所以想让django支持则需安装第三方组件,如下:

pip install channels -i https://mirrors.aliyun.com/pypi/simple/

daphne介绍参考链接:

  • https://pypi.org/project/daphne/
  • http://masnun.rocks/2016/11/02/deploying-django-channels-using-daphne/
  • https://www.osgeo.cn/django/howto/deployment/asgi/daphne.html

channels介绍参考链接:

  • https://channels.readthedocs.io/en/latest/introduction.html
django示例项目配置

本次示例代码目录结构如下图:

avatar

  1. settings.py文件配置
# 注册channels
INSTALLED_APPS = [
    'channels',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'corsheaders',
    'apps.test01',
    'apps.chat'
]
# 添加asgi application的配置
ASGI_APPLICATION = "my_web01.routing.application" # 方式二, 本人采用此方式
# ASGI_APPLICATION = "my_web01.asgi.application" # 方式一,默认

# 以下配置为channel_layer内容,请参考channels参考链接其中有实现群聊的代码
# 因为实现群聊所以用到channel_layer,如果实现简单的功能以下配置可以暂时不用
#信道层是一种通信系统。它允许多个消费者实例彼此交谈,以及与Django的其他部分交谈。
#通道层提供以下抽象:
#通道是一个可以将邮件发送到的邮箱。每个频道都有一个名称。任何拥有频道名称的人都可以向频道发送消息。
#一组是一组相关的通道。一个组有一个名称。任何具有组名称的人都可以按名称向组添加/删除频道,并向组中的所有频道发送消息。无法枚举特定组中的通道。
#每个使用者实例都有一个自动生成的唯一通道名,因此可以通过通道层进行通信。
CCHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels_redis.core.RedisChannelLayer',
        'CONFIG': {
            #"hosts": [('127.0.0.1', 6379)],
            # "hosts": ["redis://127.0.0.1/14"],
            # 此处的IP为redis是因为使用容器编排创建redis容器时指定 links:- redis
            # 即redis与IP创建了对应关系
            "hosts": ["redis://:123456@redis:6379/14"],
            # "hosts": [("redis",6379)],
        },
        # "CONFIG": {
        #     "hosts": ["redis://:password@IP:6379/0"],
        #     "symmetric_encryption_keys": [SECRET_KEY],
        # },
    },
}
  • 解释说明:

    ​ 特别是对于大型站点,可以配置像 nginx 这样的生产级 HTTP 服务器,
    以根据路径将请求路由到(1)用于普通 HTTP 请求的生产级 WSGI 服务器(如 Gunicorn+Django)
    或(2)生产- 级 ASGI 服务器,例如用于 WebSocket 请求的 Daphne+Channels。

    ​ 请注意,对于较小的站点,可以使用更简单的部署策略(asgi包含http与websocket服务)
    其中 Daphne 服务所有请求 - HTTP 和 WebSocket - 而不是拥有单独的 WSGI 服务器。

  1. asgi.py文件配置(与wsgi同级目录,没有则新建asgi文件)
# 方式二(目的只实现websocket的长连接,所有的http请求还是走wsgi)
import os
import django
from channels.routing import get_default_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'my_web01.settings')
django.setup()
application = get_default_application()
# 方式一(既包含http又包含websocket)
# import os
# from channels.auth import AuthMiddlewareStack
# from channels.routing import ProtocolTypeRouter, URLRouter
# from channels.security.websocket import AllowedHostsOriginValidator
# from django.core.asgi import get_asgi_application
# os.environ.setdefault("DJANGO_SETTINGS_MODULE", "my_web01.settings")
# django_asgi_app = get_asgi_application()
#
# from apps.chat import routing
#
# application = ProtocolTypeRouter({
#   "http": django_asgi_app,
#   "websocket": AllowedHostsOriginValidator(
#         AuthMiddlewareStack(
#             URLRouter(
#                 routing.websocket_urlpatterns
#             )
#         )
#     ),
# })

3.routing.py文件配置(与asgi文件同级目录下新建即在settings文件中的配置的文件位置)

from channels.routing import ProtocolTypeRouter
from apps.chat import routing
application = ProtocolTypeRouter({
    'websocket': routing.websocket_urlpatterns
})

4.apps/chat/routing.py文件

# routing文件效果等同于urls.py文件
from django.urls import re_path
from channels.routing import URLRouter
from . import consumers

# 我们调用as_asgi()classmethod 是为了获得一个 ASGI 应用程序,
# 该应用程序将为每个用户连接实例化我们的消费者实例。这类似于 Django 的as_view(),
# 它对每个请求的 Django 视图实例扮演相同的角色。
# 本示例未使用as_asgi()因为django版本为低版本

websocket_urlpatterns = URLRouter([
    # 所有websocket以ws/开始是约定俗成的
    re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer), 
])

5.apps/chat/consumers.py文件

# 简单示例代码
# consumers文件效果等同于views.py文件
import json
from channels.generic.websocket import WebsocketConsumer


class ChatConsumer(WebsocketConsumer):
    def __init__(self, *args, **kwargs):
        super(ChatConsumer, self).__init__(*args, **kwargs)
        self.room_name = ''
        self.room_group_name = ''

    def connect(self):
        # 获取传参参数或者header内容则通过self.scope
        print('websocket连接的参数内容:', self.scope)
        self.room_name = self.scope['url_route']['kwargs']['room_name']
        self.room_group_name = 'chat_%s' % self.room_name
        self.accept()

    def disconnect(self, close_code):
        self.close()  # 断开连接

    def receive(self, text_data=None, bytes_data=None):
        data = text_data or bytes_data
        if data:
            data = json.loads(data)
            print('接收到的参数:', data)
            msg = data.get('msg')
            if msg == 'ok':
                self.send(json.dumps({'content': '收到了ok'}))
            else:
                self.send(json.dumps({'content': f'收到了{msg}'}))

以上就是简单的demo;python manage.py runserver之后出现如下图“ASGI/Channels”则表明集成了channels支持http和

websocket(长连接)协议了。

avatar

使用postman测试长连接请求

1.打开postman点击左上角"New",按照下图所选

2.postman测试连接,并发送消息如下图:

文档最后,当然关于websocket的技术知识点还有很多,本文只是快速简单了解其过程。相信通过简单的测试,对django支持长连接有所了解,如果深入的了解请访问文章开始博主分享的官网地址。

分享他人github 简单示例

https://github.com/py3study/django3_websocket

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐