- 后端
- WebSocket
- 异步编程
【免费下载链接】channels
Developer-friendly asynchrony for Django
Channels 1.1.4 是 Channels 1.1 系列的一个纯 bugfix 版本,于 2017 年 6 月 15 日发布,聚焦于修复消息积压下的重试行为、改善 Worker 进程退出体验、修正QUERY_STRING编码,并为测试体系引入ChannelServerLiveTestCase、Origin 头校验装饰器与TEST_CONFIG等实用能力。本文以官方发布说明为主体,结合当前仓库(channels/__init__.py中版本标识为 4.2.0)的源码实现,逐条还原这些修复与增强的底层机制,帮助读者理解 Channels 演进过程中沉淀下来的关键设计。
版本概况
根据 docs/releases/1.1.4.rst 的发布说明,1.1.4 属于 1.1 系列的 bugfix 版本,发布于 2017 年 6 月 15 日:
- Major Changes(主要变更):无;
- Minor Changes & Bugfixes(次要变更与缺陷修复):共 7 项,覆盖消息积压重试、Worker 信号处理、请求元数据编码、测试客户端改进、Live 测试、安全校验与测试层配置;
- Backwards Incompatible Changes(向后不兼容变更):无,升级风险极低。
从 CHANGELOG.txt 的变更记录可以看到,"Pending messages correctly handle retries in backlog situations"(待处理消息在积压场景下正确处理重试)同样被收录,说明这是 1.1.4 中值得重点关注的修复项。值得注意的是,当前仓库快照中channels/__init__.py标识的版本已是 4.2.0,本文所述能力在现代版本中以演进后的形态继续存在,可作为理解 Channels 设计脉络的历史坐标。
修复一:积压场景下待处理消息的重试处理
发布说明第一条指出:Pending messages correctly handle retries in backlog situations(待处理消息在积压情况下能正确处理重试)。
在 Channels 的消息传递模型中,Worker 从通道层取走消息后,若处理失败需要将其重新放回队列(retry),而"积压"(backlog)意味着通道队列已满、容量不足。此前的实现中,重试放入的待处理消息在积压场景下可能被错误丢弃或重复消费;1.1.4 修正了这一行为,确保重试消息在队列满时仍能按预期被再次排队。
从当前仓库的 channels/layers.py 可以看到,现代通道层的容量控制继承自BaseChannelLayer:__init__接收expiry(默认 60 秒)、capacity(默认 100)与channel_capacity(按通道名 glob 模式覆盖容量的字典),get_capacity()会按顺序返回第一个匹配模式对应的容量。在InMemoryChannelLayer.send()中,消息通过queue.put_nowait()写入受限队列,当队列已满(asyncio.queues.QueueFull)时抛出ChannelFull异常,由上层调用者决定如何重试。这一"容量受限 + 显式异常"的机制,正是保证积压场景下重试语义正确的基础:生产者必须感知积压并决定重试策略,而不是静默丢失消息。
修复二:线程模式下 Worker 响应 Ctrl-C 并优雅退出
发布说明第二条:Workers in threading mode now respond to ctrl-C and gracefully exit(线程模式下的 Worker 现在能响应 Ctrl-C 并优雅退出)。
Channels 的runworker管理命令(位于 channels/management/commands/runworker.py)支持异步单进程与多线程两种执行模式。在 1.1.4 之前,线程模式下的 Worker 对SIGINT(Ctrl-C)的处理不完善,终端中断可能导致进程挂死或资源未清理;本次修复让线程模式下的 Worker 也能像异步模式一样捕捉中断信号、停止接收新消息并安全退出。现代实现中 Worker 的优雅退出链路更完整地依赖异步任务的取消与循环退出,这一修复是其早期铺垫。
修复三:request.meta['QUERY_STRING']始终正确编码
发布说明第三条:request.meta['QUERY_STRING']is now correctly encoded at all times(request.meta['QUERY_STRING']现在任何情况下都正确编码)。
该修复针对 HTTP 请求进入消费者时,scope中的查询字符串在转发到 Djangorequest.meta后可能出现的编码不一致问题(例如原始字节串与非 ASCII 查询参数处理差异)。1.1.4 统一了QUERY_STRING的编码路径,保证其始终以正确编码呈现给下游视图与中间件。从当前仓库结构看,这一修复已融入后续 HTTP 处理链路的重构(详见 channels/generic/http.py 与 channels/http.py),属于早期稳定 HTTP 桥接语义的重要一步。
测试能力增强(一):ChannelServerLiveTestCase
发布说明指出:ChannelServerLiveTestCaseadded, allows an equivalent of the DjangoLiveTestCase——新增ChannelServerLiveTestCase,提供与 Django 自带LiveServerTestCase等价的测试能力。
在 Django 生态中,LiveServerTestCase会在测试期间启动真实的 HTTP 服务器,供 Selenium 等浏览器自动化框架访问。1.1.4 为 Channels 补齐了对应物:在真实运行环境(而非测试客户端模拟)中启动一个监听真实端口的 Daphne 服务进程,从而支持端到端的实时通信测试。
在当前仓库中,该类以ChannelsLiveServerTestCase的形态存在于 channels/testing/live.py,其关键实现值得展开:
- 继承自 Django 的
TransactionTestCase,默认host = "localhost",通过ProtocolServerProcess = DaphneProcess(来自daphne.testing)在独立进程中启动协议服务器; - 提供
live_server_url(http://host:port)与live_server_ws_url(ws://host:port)两个属性,分别给出 HTTP 与 WebSocket 的真实访问地址; static_wrapper默认使用ASGIStaticFilesHandler,serve_static = True时在测试中托管静态文件,便于浏览器端完整加载页面资源;_pre_setup()中会检测内存数据库:由于 Live 服务器运行在独立进程,内存数据库(如 SQLite 的:memory:)无法跨进程共享,因此遇到内存数据库时直接抛出ImproperlyConfigured提示改用文件型数据库;- 通过
modify_settings(ALLOWED_HOSTS={"append": self.host})动态放行测试主机名,避免 Host 校验拦截真实请求; _post_teardown()中终止并等待服务器进程退出,完成资源回收。
该测试类的导出位于 channels/testing/init.py,与ApplicationCommunicator、HttpCommunicator、WebsocketCommunicator并列,构成完整的测试工具集;具体用法可参考 docs/topics/testing.rst。
测试能力增强(二):CHANNEL_LAYERS新增TEST_CONFIG
发布说明指出:NewTEST_CONFIGsetting inCHANNEL_LAYERSthat allows varying of the channel layer for tests——CHANNEL_LAYERS设置中新增TEST_CONFIG,允许为测试环境单独指定通道层配置(例如切换到另一个 Redis 实例)。
该设计的价值在于:生产环境与测试环境的通道层需求往往不同(隔离性、清理成本、依赖可用性),在CHANNEL_LAYERS中按name提供独立的TEST_CONFIG,即可让测试代码通过同一套别名拿到专属的通道层实例,而无需改动生产配置。
在当前仓库的 channels/layers.py 中,这一机制由ChannelLayerManager.make_test_backend()实现:
def make_test_backend(self, name): """ Instantiate channel layer using its test config. """ try: config = self.configs[name]["TEST_CONFIG"] except KeyError: raise InvalidChannelLayerError("No TEST_CONFIG specified for %s" % name) return self._make_backend(name, config)关键行为可从源码确认:
_make_backend()从配置中读取BACKEND(后端类的导入路径)并实例化;因此TEST_CONFIG与常规CONFIG具有相同的结构——即{"BACKEND": ..., "hosts": [...]}形式,只是值可以不同;- 若某别名未定义
TEST_CONFIG,调用测试后端时会抛出InvalidChannelLayerError("No TEST_CONFIG specified for %s"),明确提示缺少配置; set()方法支持在测试中直接替换别名指向的通道层实例并返回旧实例,配合TEST_CONFIG可灵活切换测试后端。
由此,测试中"使用内存层、测试专用 Redis 实例或隔离队列"均可通过纯配置方式声明,无需在代码中硬编码分支逻辑。
测试能力增强(三):测试客户端改进
发布说明以"Test client improvements(测试客户端改进)"概括本版本对测试客户端的整体增强。结合上文两项能力可以推断,1.1.4 围绕 Channels 测试体验做了系统性收尾:既有新的ChannelServerLiveTestCase支撑真实进程级测试,也有TEST_CONFIG让通道层本身可切换。这两者加上既有 Communicator 体系(channels/testing/application.py、channels/testing/http.py、channels/testing/websocket.py),共同构成了 Channels 覆盖"单元级(Communicator)→ 集成级(真实服务器)→ 环境级(通道层替换)"三个层次的测试能力栈。
安全增强:检查Origin头的装饰器(allowed_hosts_only)
发布说明指出:Decorator added to checkOriginheaders (allowed_hosts_only)——新增用于检查Origin头的装饰器allowed_hosts_only。
该能力用于 WebSocket 场景的跨站防护:浏览器发起的 WebSocket 握手携带Origin头,服务端应校验其是否来自可信域名,防止恶意站点通过浏览器向服务端发起跨站 WebSocket 连接。1.1.4 以装饰器形式提供这一检查,将其命名为allowed_hosts_only,即"仅允许ALLOWED_HOSTS中的主机"。
在现代版本中,该功能演进为 channels/security/websocket.py 中的OriginValidator类与AllowedHostsOriginValidator工厂函数,可以从实现细节反推其校验逻辑:
OriginValidator.__call__要求scope["type"] == "websocket",否则抛出ValueError——说明该校验器只能应用于 WebSocket 连接;- 从
scope["headers"]中提取origin头并urlparse解析;解析失败或缺失时,valid_origin()在allowed_origins不含通配符"*"的情况下直接拒绝; validate_origin()支持三种模式:"*"放行一切;不带 scheme 的域名(如example.com)通过 Django 的is_same_domain做域名匹配;带 scheme 的完整 origin 则同时比较 scheme、hostname 与端口(HTTP/WS 默认 80,HTTPS/WSS 默认 443,见get_origin_port());- 域名以
.开头(如.example.com)表示该域名及其所有子域; - 校验失败时由
WebsocketDenier(一个继承AsyncWebsocketConsumer的消费者)直接close()拒绝连接; AllowedHostsOriginValidator自动读取settings.ALLOWED_HOSTS,并在DEBUG且未配置时默认放行localhost、127.0.0.1、[::1]。
因此,allowed_hosts_only的核心语义(依据 DjangoALLOWED_HOSTS白名单校验 WebSocketOrigin)在 1.1.4 中即已确立,并延续为现代版本的默认安全护栏,使用说明可参考 docs/topics/security.rst。
向后兼容性
发布说明明确:1.1.4无任何向后不兼容变更(Backwards Incompatible Changes: None)。所有改动均为缺陷修复与增量增强,1.1.x 用户可直接升级而无需修改既有代码或配置;新增的TEST_CONFIG为可选键,未配置时仅在测试代码主动请求测试后端时才会触发明确报错。
延伸阅读
- CHANGELOG.txt:完整版本变更历史,可对照 1.1.4 前后各版本的能力演进;
- docs/releases/index.rst:系列发布说明索引;
- docs/topics/testing.rst:Communicator 与异步测试的完整用法;
- channels/testing/live.py:
ChannelsLiveServerTestCase的实现细节; - channels/layers.py:通道层配置解析(
CONFIG/TEST_CONFIG)与容量控制; - channels/security/websocket.py:
OriginValidator与现代版AllowedHostsOriginValidator; - tests/test_layers.py:通道层行为对应的测试用例。
- 后端
- WebSocket
- 异步编程
【免费下载链接】channels
Developer-friendly asynchrony for Django
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考