- 后端
- WebSocket
- 异步编程
【免费下载链接】channels
Developer-friendly asynchrony for Django
导读:本文以官方发布说明 docs/releases/2.1.5.rst 为骨架,解读 Channels 2.1.5 这一 bugfix 版本的核心修复——Django 中间件缓存机制在 Django 1.11 与 Django 2.0 上恢复正常工作(此前的 2.1.4 仅支持 Django 2.1),并借机梳理 Channels 的 ASGI 中间件架构(BaseMiddleware、Session/Cookie/Auth 中间件栈)与版本兼容策略。读完本文,你将掌握 2.1.5 修复的来龙去脉、Channels 中间件的装载与缓存原理,以及如何正确编写和使用 ASGI 中间件。
一、发布概览:又一个无破坏性变更的 bugfix 版本
Channels 2.1.5 是 2.1 系列的 bugfix 版本,官方发布说明内容如下:
- Bugfixes & Small Changes:Django middleware caching now works on Django 1.11 and Django 2.0. The previous release only ran on 2.1.(Django 中间件缓存现在在 Django 1.11 和 Django 2.0 上正常工作,此前的版本只运行在 2.1 上。)
- Backwards Incompatible Changes:None.(无破坏性变更)
也就是说,2.1.5 是一个纯修复型版本,不改变任何公开 API 与行为约定,2.1.x 系列的既有用户可以直接升级而无需调整代码。完整的 2.1.x 发布记录可在 docs/releases/index.rst 中查看。
二、核心修复:Django 中间件缓存兼容 Django 1.11 / 2.0
2.1 修复背景:2.1.4 首次引入中间件缓存
要理解 2.1.5 的修复动机,必须先回到上一个版本 docs/releases/2.1.4.rst。2.1.4 发布说明中明确写道:
Django middleware is now cached rather than instantiated per request resulting in a significant speed improvement. Some middleware took seconds to load and as a result Channels was unusable for HTTP serving before.
从 2.1.4 起,Channels 一改"每个请求都重新实例化 Django 中间件"的做法,改为缓存中间件实例,带来了显著的性能提升。此前部分中间件加载耗时高达数秒,导致 Channels 在 HTTP 服务场景下几乎不可用——缓存机制正是为了解决这个痛点而引入。
2.2 2.1.5 的修复内容与意义
2.1.5 发布说明的表述非常精确:中间件缓存"现在在 Django 1.11 和 Django 2.0 上正常工作",而"此前的版本只运行在 2.1 上"(The previous release only ran on 2.1)。
由此可以推断:
- 2.1.4 引入的中间件缓存实现依赖了 Django 2.1 特有的某些行为或内部接口;
- 当应用运行在 Django 1.11 或 2.0 上时,该缓存无法生效或出现兼容问题;
- 2.1.5 针对旧版本 Django 做了兼容处理,使缓存机制在 Django 1.11、2.0、2.1 三个受支持的大版本上都能正常工作。
这对当时仍停留在 Django 1.11 / 2.0 的项目意义重大:它们无需升级 Django 即可获得中间件缓存的性能收益。需要说明的是,由于当前仓库主线已演进至 4.x(channels/init.py 中__version__ = "4.2.0"),2.1.5 当时的具体补丁代码已不在当前代码中直接可见;本文依据的是作为历史文档保留的发布说明本身,其结论不受影响。
2.3 为什么缓存中间件如此关键
Django 的MIDDLEWARE配置项是一个处理链,每个请求/响应都要穿过它。在传统同步 WSGI 世界中,中间件的创建与调度由框架统一管理;而在 Channels 的异步 ASGI 世界中,Channels 需要自行编排中间件如何包裹内部 ASGI 应用。若每个请求都重新执行中间件的导入与实例化,开销将随请求量线性放大——官方说明中"部分中间件加载耗时以秒计"的描述直接解释了为什么 Channels 在修复前"对 HTTP 服务不可用"。
缓存中间件实例的本质是:构造一次、跨请求复用。这带来一个重要的副作用——中间件实例上的可变状态会跨请求泄漏,因此 Channels 对中间件的设计有严格约束(详见下文 3.1)。
三、Channels 的 ASGI 中间件架构(源码视角)
尽管 2.1.5 是历史版本,当前仓库 4.x 中的中间件架构延续了同一套设计思路,二者可以相互印证。以下均可在当前仓库源码中找到对应实现。
3.1BaseMiddleware:ASGI 中间件基类与"无状态"约定
channels/middleware.py 定义了所有 Channels 中间件的基类:
class BaseMiddleware: """ Base class for implementing ASGI middleware. Note that subclasses of this are not self-safe; don't store state on the instance, as it serves multiple application instances. Instead, use scope. """ def __init__(self, inner): """ Middleware constructor - just takes inner application. """ self.inner = inner async def __call__(self, scope, receive, send): """ ASGI application; can insert things into the scope and run asynchronous code. """ # Copy scope to stop changes going upstream scope = dict(scope) # Run the inner application along with the scope return await self.inner(scope, receive, send)这里有三个关键设计点:
- 洋葱式调用链:构造器只接收
inner(下一个 ASGI 应用),__call__是异步入口,可在调用内部应用前向scope注入数据、执行异步逻辑,从而在"外部"拦截并增强连接或请求。 - scope 拷贝:
scope = dict(scope)防止对 scope 的修改向上游泄漏,保证中间件链各层之间的隔离。 - 严禁实例状态:源码注释明确警告——子类"不是自安全的;不要在实例上存储状态,因为它服务多个应用实例,应使用 scope"。这正与 2.1.4 引入的"中间件实例缓存复用"设计直接呼应:实例被缓存复用时,任何挂在
self上的可变状态都会在多次请求/连接之间交叉污染,因此一切请求级状态都必须写入随连接独立存在的scope。
3.2 内置中间件与"中间件栈"快捷组合
Channels 内置了几个常用中间件,并提供了栈式组合的快捷函数:
- CookieMiddleware 与 SessionMiddleware:channels/sessions.py 实现。
SessionMiddleware将 Django session(基于 HTTP Cookie)解析进scope,其文档字符串要求CookieMiddleware 必须位于栈的更高层;若 scope 中缺少 cookies,SessionMiddleware会抛出 "No cookies in scope - SessionMiddleware needs to run inside of CookieMiddleware."。同时提供SessionMiddlewareStack(inner)快捷函数,直接返回CookieMiddleware(SessionMiddleware(inner))。 - AuthMiddleware 与 AuthMiddlewareStack:channels/auth.py 实现。
AuthMiddleware从 Django session 中填充scope["user"],并依赖 SessionMiddleware;AuthMiddlewareStack(inner)的返回值清晰地展示了标准组合顺序:
def AuthMiddlewareStack(inner): return CookieMiddleware(SessionMiddleware(AuthMiddleware(inner)))- OriginValidator 与 AllowedHostsOriginValidator:channels/security/websocket.py 实现。前者校验 WebSocket 连接的 Origin 头是否在允许列表中(支持精确匹配、
.前缀子域名匹配与*通配),后者将其配置为使用settings.ALLOWED_HOSTS(DEBUG 模式下回退到 localhost 等)。2.1.4 发布说明中"改进非法 Origin 头的错误信息"即与此模块相关。
3.3 版本兼容的代码路径:Channels 的惯用做法
Channels 长期以来需要同时支持多个 Django 大版本,源码中随处可见按django.VERSION分支的兼容写法。例如 channels/sessions.py 的save_session:
if django.VERSION >= (5, 1): await self.scope["session"].asave() else: await database_sync_to_async(self.scope["session"].save)()从这一惯例可以推断,2.1.5 修复中间件缓存在 Django 1.11 / 2.0 上的兼容性问题,采用的就是类似的思路:识别出 2.1.4 缓存实现中对 Django 2.1 特定行为的依赖,在旧版本上回退或改走等价的代码路径,从而让缓存机制对三个受支持版本一致生效。这正是 Channels 发布说明中 "now works on Django 1.11 and Django 2.0" 一语的源码级含义。
四、2.1.x 系列版本脉络
将 2.1.5 放入整个 2.1 系列中,可以更清晰地理解它的位置:
- 2.1.4:引入 Django 中间件缓存(HTTP 服务性能大幅提升)、修复 ChannelServerLiveTestCase 静态文件、改进 Origin 头错误信息、
runserver日志接入 Django logging、通用 consumer 支持channel_layer_alias、改进scope['user']过早访问时的报错信息(详见 docs/releases/2.1.4.rst)。 - 2.1.5:中间件缓存的兼容性修复,覆盖 Django 1.11 与 2.0(本文主题)。
- 2.1.6:修复
HttpCommunicator的查询字符串解析、AsyncHttpConsumer提供与其他 consumer 一致的 channel layer 属性、避免 Daphne 延迟导入错误(详见 docs/releases/2.1.6.rst)。 - 完整的 2.1.x 发布记录索引见 docs/releases/index.rst。
五、升级与兼容性建议
- 升级安全性:2.1.5 无破坏性变更(Backwards Incompatible Changes: None),2.1.x 用户可直接升级。
- 受支持的 Django 版本:该版本同时覆盖 Django 1.11、2.0、2.1——这正是 2.1.5 与 2.1.4 的关键差异(后者缓存功能仅限 Django 2.1)。
- 注意版本上下文:当前仓库主线为 4.2.0(见 channels/init.py),setup.cfg 显示现代版本要求
Django>=4.2、Python>=3.8,并支持 Python 3.8–3.13。本文所述 2.1.5 发布说明描述的是 Django 1.11–2.1 时代的兼容行为,属于历史文档参考,不应套用于 4.x 当前运行环境。 - 中间件编写原则:无论哪个版本,遵循 channels/middleware.py 的约定——不把状态存到实例上,全部放入
scope;需要组合多个中间件时,优先使用SessionMiddlewareStack/AuthMiddlewareStack等快捷函数保证顺序正确。
六、总结
Channels 2.1.5 是一个小而关键的 bugfix 版本:它将 2.1.4 引入的 Django 中间件缓存的适用范围从 Django 2.1 扩展到 Django 1.11 与 2.0,让更多项目能够享受"中间件实例化一次、跨请求复用"的性能收益,同时保持零破坏性变更。围绕这一修复,Channels 的中间件体系呈现出清晰的设计哲学:BaseMiddleware基类规定洋葱式调用链与"状态只进 scope"的约束,CookieMiddleware → SessionMiddleware → AuthMiddleware的栈式组合定义了标准会话与认证链路,而对不同 Django 大版本的兼容则通过版本条件分支的代码路径实现。对于今天仍在阅读 2.1.x 历史的开发者而言,这一版本既是一份可追溯的性能优化案例,也是理解 Channels ASGI 中间件架构的最佳切入点。
- 后端
- WebSocket
- 异步编程
【免费下载链接】channels
Developer-friendly asynchrony for Django
相关推荐
用Voyager文件夹驯服混乱的AI对话:拖拽、嵌套与自定义颜色的完整组织指南
用Voyager文件夹驯服混乱的AI对话:拖拽、嵌套与自定义颜色的完整组织指南 Voyager 是一款面向 Gemini、AI Studio、Claude 与
后端WebSocket异步编程SPECTER2_aug2023refresh_base实战教程:用Python实现论文相似度计算
SPECTER2_aug2023refresh_base实战教程:用Python实现论文相似度计算 SPECTER2_aug2023refresh_base是一
FastAPI 高级中间件指南:add_middleware 接入任意 ASGI 中间件与内置中间件详解
FastAPI 高级中间件指南:add_middleware 接入任意 ASGI 中间件与内置中间件详解 FastAPI 以 Starlette 为基座并完整实
后端Web框架API设计
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考