WebSocket 允许服务端主动向客户端推送数据。在 WebSocket 协议中,客户端浏览器和服务器只需要完成一次握手就可以创建持久性的连接,并在浏览器和服务器之间进行双向的数据传输。

WebSocket建立连接

WebSocket协议是基于HTTP协议来建立传输层的TCP链接

客户端通过HTTP协议发送 GET 请求,
请求头标识 升级为 WebSocket协议:

  1. Connection Upgrade
  2. Upgrade: websocket
  3. Sec-WebSocket-Version: 13 # 客户端使用的WebSocket版本
  4. Sec-WebSocket-Key: zrAJFRR2ch4lvBSDPkbcOQ== # 一个Base64 编码值,由浏览器生成用于升级 request
  5. Sec-WebSocket-Extensions: #客户端表示的协议级扩展

服务端返回:

  1. # 表示正在将客户端发送的 request 转换成 header 里的 Upgrade 字段指定的协议
  2. Status Code: 101 Switching Protocols
  3. # 如果响应里包含了 Upgrade 字段表示已经转换成功
  4. Connection: upgrade
  5. Upgrade: WebSocket
  6. #表示服务器接受了客户端的请求,由 request 里的 Sec-WebSocket-Key 计算得出
  7. Sec-WebSocket-Accept
  8. # 如果支持客户端请求的版本号,响应头里就没有 Sec-WebSocket-Version 字段
  9. # 如果不支持,响应头里就会包含支持的版本号
  10. # Sec-WebSocket-Version 11111111

优缺点

优点:

  • 支持双向通信,实时性更强
  • 数据格式比较轻量,性能开销小,通信高效
    • 协议控制的数据包的头部较小,服务端到客户端推送数据的时候,包头只有2~10个字节大小,客户端到服务端也只有 2~10个字节加额外4个字节的掩码
    • 如果不使用WebSocket,用AJAX轮询来实现,但是用户多了对后端消耗较大
  • 支持扩展。用户可以扩展协议或者实现自定义的子协议(比如支持自定义压缩算法)

缺点:

  • 少部分浏览器不支持,浏览器支持的程度与方式有区别
  • 长连接对后端处理业务的代码稳定性要求更高,后端推送功能相对复杂
  • 成熟的 HTTP 生态下有大量的组件可以复用,WebSocket较少

应用场景

  • 及时聊天通信,网站消息通知
  • 在线协同编辑,如石墨,语雀,腾讯文档
  • 多玩家在线游戏,视频弹幕,股票基金实时报价

Django中的使用

  • 如何区分路由 HTTP 请求 和 WebSocket 请求
  • 如何兼容Django 的认证系统
  • 如何接收和推送WebSocket消息

使用官方推荐包 channels:https://github.com/django/channels

Django Channels 原理

Django Channels 是一个为Django提供异步扩展的库,通常主要用来提供 WebSocket的支持和后台任务
image.png
Protocol type Router:对不同的协议解析

  • 如果是 HTTP 协议就传递给 HTTP Router,也就是Django 的 url.py
  • 如果是 WebSocket 协议就转发给 WebSocket Router,对应Channel 中的 routings.py,然后传递给 Channels 视图 consumer.py 处理
  • 对于其他类型的协议,通过对应的 Router,传递给对应的消费者

image.png
Interface Server: 负责对协议进行解析,将不同协议分发到不通的Channel
Channel Layer:频道层,可以是FIFO队列,通常使用Redis
Consumer:消费者,接收和处理消息

channels 文件和配置

  • asgi.py: 介于网络协议服务和Python应用之间的标准接口,能够处理多重通用协议类型,包括HTTP,HTTP2和WebSocket,相当于Django 中的 wsgi.py
  • channel_layers: 在 settings.py 中 配置,类似于一个通道,发送者(producer)在一端发送消息,消费者(consumer)在另一端监听
  • routings.py: 相当于Django 中的 urls.py
  • consumers.py: 相当于Django 中的views.py

WSGI 和 ASGI 的区别

  • WSGI (Python Web Server Gateway Interface):为Python语言定义的Web服务器和Web应用程序或矿建之间的一种简单而通用的接口
    • uWSGI: 符合WSGI 标准的 web 服务软件
  • ASGI (Asynchroonous Server Gateway Interface):异步服务网关接口,一个介于网络协议服务和Python应用之间的标准接口,能够处理多种通用的协议类型,包括 HTTP,HTTP2 和 WebSocket
    • Daphne:符合ASGI 标准的 web 服务软件
  • WSGI 和 ASGI 的区别:WSGI 是基于HTTP协议模式的,不支持WebSocket,而ASGI就是为了支持Python常用的WSGI所不支持的新的协议标准,即ASGI是WSGI的扩展,并且能够通过asyncio异步运行

在项目中集成Channels:
官方文档:
https://channels.readthedocs.io/en/latest/deploying.html#configuring-the-asgi-application

  1. 在与 wsgi.py 文件的同级目录下创建 asgi.py
  1. """
  2. ASGI entrypoint. Configures Django and then runs the application
  3. defined in the ASGI_APPLICATION setting.
  4. """
  5. import os
  6. import django
  7. from channels.routing import get_default_application
  8. os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
  9. django.setup()
  10. application = get_default_application()
  1. 把 channels 加入 INSTALLED_APPS
  1. INSTALLED_APPS = [
  2. 'channels',
  3. ...
  4. ]
  1. 配置 CHANNEL_LAYERS

    1. # 频道层的缓存
    2. CHANNEL_LAYERS = {
    3. "default": {
    4. "BACKEND": "channels_redis.core.RedisChannelLayer",
    5. "CONFIG": {
    6. "hosts": ['redis://127.0.0.1:6379/3'],
    7. },
    8. },
    9. }
  2. 在app里添加 consumers.py

  • consumers.py 用来开发符合 ASGI 接口规范的应用
  • views.py 用来开发符合 WSGI 接口规范的应用

image.png
一般继承自 AsyncJsonWebsocketConsumer 异步json Consumer