- 后端
- 消息队列
- 任务调度
【免费下载链接】bullmq
BullMQ - Message Queue and Batch processing for NodeJS, Python, .NET, Elixir, Rust and PHP based on Redis or PostgreSQL
导读
本文以 BullMQ 仓库中 Python 客户端变更日志 为骨架,梳理 v3.0.0 至 v3.2.6 的版本演进脉络:核心主线是 v3 引入的**可插拔队列后端(pluggable queue backends)**架构——IQueueBackend抽象让同一套高层 API 同时运行在 Redis 与 PostgreSQL 之上;随后 v3.1 与 v3.2 分别带来处理器控制的延迟调度(DelayedError+Job.moveToDelayed)与去重查询能力(Queue.getDeduplicationJobId)。读完本文,你将理解 v3 破坏性变更的迁移要点、后端抽象的工作原理,以及这些新特性在实际代码中的调用方式。
版本总览:v3 主线与发布时间线
截至 changelog 记录的最新版本 v3.2.6(2026-09-21),Python 客户端 v3 系列沿一条清晰主线演进:
| 版本 | 时间 | 类型 | 核心内容 |
|---|---|---|---|
| 3.0.0 | 2026-07-30 | 主版本 | 发布可插拔队列后端:引入IQueueBackend抽象、Redis 与 PostgreSQL 后端,含多项破坏性变更 |
| 3.0.1 | 2026-07-31 | 修复 | 补全发布包缺失的 SQL 文件(影响 elixir/python) |
| 3.0.2 | 2026-07-31 | 修复 | redis 依赖升级至 v7.4.1;PostgreSQL schema 对象去除冗余的bullmq_前缀 |
| 3.0.3 | 2026-08-01 | 修复 | Python 依赖批量更新 |
| 3.0.4 | 2026-08-05 | 修复 | job scheduler 可推进并支持 PostgreSQL 后端 |
| 3.0.5 | 2026-08-25 | 修复 | worker 存储失败原因时不再做额外 JSON 编码 |
| 3.0.6 | 2026-08-25 | 性能 | 处理 deferred failures 时不再计入速率限制(同步 elixir/rust/dotnet) |
| 3.1.0 | 2026-08-28 | 特性 | 新增DelayedError与Job.moveToDelayed,支持处理器控制的延迟 |
| 3.1.1 | 2026-08-29 | 修复 | 将maximumBlockTimeout委托给后端;Python 依赖更新 |
| 3.2.0 | 2026-08-31 | 特性 | 新增Queue.getDeduplicationJobId方法 |
| 3.2.1 ~ 3.2.6 | 2026-09 上旬 | 修复 | 依赖更新;v3.2.3 修复 deduplication key 残留问题 |
整条 v3 线的设计意图非常集中:用后端抽象统一多语言客户端的数据存储语义,并围绕去重、延迟、调度补齐与 Node.js 客户端对齐的能力。
3.0.0:可插拔队列后端架构
IQueueBackend 抽象:高层类不再直连数据存储
v3 的核心变革是引入IQueueBackend抽象。在 Python 实现中,这一契约由 python/bullmq/backend.py 中的Backend抽象基类表达:它把队列语义("move job to active"、"extend lock"、"promote job"……)与底层数据存储解耦,高层类(Queue、Worker、Job、FlowProducer)只依赖该抽象,从不直接操作数据存储客户端。
从源码结构看,该抽象按职责分组定义了几十项操作:
- 连接生命周期:
waitUntilReady、close、disconnect、setName; - 队列身份与建键:
qualifiedName、keys、toKey、clientName; - 添加任务:
addJob、addJobs(批量)、addFlow(跨队列原子插入任务树); - 状态迁移:
moveToActive、moveToCompleted、moveToFailed、moveToDelayed、moveToWaitingChildren、retryJob、reprocessJob、promote、moveStalledJobsToWait; - 批量管理:
retryJobs、promoteJobs、pause、drain、cleanJobsInSet、obliterate、remove; - 锁:
extendLock、extendLocks; - 任务变更与查询:
updateData、updateProgress、changePriority、addLog、getState、getJobData、getJobLogs、getCounts、getRanges等; - 调度器:
addJobScheduler、updateJobSchedulerNextMillis、removeJobScheduler、getJobScheduler(s)、getJobSchedulersCount; - worker 阻塞原语:
waitForJob。
抽象基类的设计有一个值得注意的细节:接口故意不暴露任何连接或事务类型——具体适配器自己持有连接,调用方从不把连接或事务穿透过某个操作(见 backend.py 的 design notes)。这让FlowProducer可以通过forQueue拿到绑定到同一连接的兄弟后端,从而跨队列原子提交任务树。
内置实现:Redis 与 PostgreSQL 双后端
内置实现有两个:python/bullmq/backends/redis_backend.py 与 python/bullmq/backends/postgres_backend.py,均实现同一契约。
- Redis 后端:直接把操作映射到 Lua 脚本与 Redis 命令。例如
getDeduplicationJobId是对de(deduplication)key 命名空间的一次普通GET(见 redis_backend.py 中注释),底层脚本注册表见 python/bullmq/redis_connection.py(如moveToDelayed对应moveToDelayed-11.lua)。 - PostgreSQL 后端:以 schema 为命名空间(
keys为空、toKey直接拼queue:type),阻塞原语基于LISTEN/NOTIFY而非轮询(见 postgres_backend.py 中的实现注释)。v3.0.2 还清理了 schema 对象中冗余的bullmq_前缀,v3.0.4 让 job scheduler 支持推进并运行在 PostgreSQL 后端之上。
注入方式:BackendFactory
后端不是由高层类自行 new 出来的,而是通过BackendFactory(Callable[..., Backend],定义于 backend.py)注入。changelog 明确指出:"The optional Connection constructor parameter is replaced by an optional BackendFactory"。默认工厂是create_redis_backend,用户可注入自定义工厂以切换到 PostgreSQL 或未来其他数据存储,且无需改动Queue/Worker/Job/FlowProducer任何一行代码。测试侧的证据可见 python/tests/postgres_backend_test.py,它验证了 Postgres 后端的maximumBlockTimeout特性(详见下文 v3.1.1 一节)。
3.0.0 破坏性变更:迁移核对清单
changelog 用一整节列出了 v3.0.0 的 BREAKING CHANGES,这是升级时最需要逐条核对的清单:
- 高层类不再暴露 Redis 内部实现:
- 可选的
Connection构造参数替换为可选的BackendFactory; Queue#client、Queue#redisVersion、Queue#databaseType、Worker#blockingClient、FlowProducer#client全部移除;- 如需访问原始 Redis 客户端,通过
getBackend()返回的RedisQueueBackend获取; Worker#waitUntilReady()现在解析为void而不是 Redis 客户端。
- 可选的
- 移除已废弃的 debounce 选项与
Job#debounceId属性:改用 deduplication 与Job#deduplicationId。对应的debounced事件也被移除,请监听deduplicated事件。 - FlowJob 区分父节点与叶子节点:父流程节点不再允许 deduplication。
- paused 状态从
JobType与Queue#getJobCounts()默认结果中移除:暂停队列中的任务以 waiting 表示。 - Redis 公共实现的部分导出被移除:
Scripts、createScripts、JobJsonRaw、RedisJobOptions不再公开,请改用后端 API。
迁移路径的总体方向是:把"从 Redis 细节出发"的用法,改为"从队列语义出发"的用法——通过后端抽象表达意图,而不是直接操作客户端、脚本或键。
3.1.0:处理器控制的延迟(DelayedError+Job.moveToDelayed)
v3.1.0 为 Python 客户端带来一个与 Node.js 客户端对齐的能力:处理器可以在执行过程中自主决定把任务推迟到未来某个时间点,而不是依赖任务的初始delay配置或失败重试的 backoff。
用法与语义
在处理器内部:
- 调用
await job.moveToDelayed(timestamp, token),其中timestamp是任务应回到wait状态的时间戳(毫秒); - 然后抛出
DelayedError,让 worker 知道"这个任务既不要完成也不要失败"。
实现位于 python/bullmq/job.py:moveToDelayed只在任务处于 active(即从处理器内部调用)时允许,计算delay = timestamp - now,并通过后端moveToDelayed(..., {"skipAttempt": True})把任务放入延迟集合——skipAttempt: True意味着这次"推迟"不计入attemptsMade,不消耗重试次数。
异常类型与 worker 的处理
DelayedError定义在 python/bullmq/custom_errors/delayed_error.py,并通过 python/bullmq/init.py 从bullmq顶层导出,可以直接from bullmq import DelayedError(有 python/tests/test_imports.py 的导入测试作证)。
在 worker 侧,python/bullmq/worker.py 的processJob中显式捕获(DelayedError, WaitingChildrenError)并直接返回——不进入moveToCompleted也不进入moveToFailed流程。也就是说:DelayedError只负责让 worker 走开,真正"停车"的是moveToDelayed调用;如果只抛异常而不调用moveToDelayed,任务不会被推迟。这一语义在 python/tests/worker_test.py 中有专门的测试用例验证(先moveToDelayed再raise DelayedError())。
典型应用场景:任务在运行中发现"条件还不满足、几分钟后再试",且希望保持锁的语义与重试次数不被浪费。
3.1.1:将maximumBlockTimeout委托给后端
v3.1.1 的修复"delegate maximumBlockTimeout to the backend (python)"让阻塞超时上限变成后端专属属性。
在抽象基类 backend.py 中,maximumBlockTimeout默认返回 10 秒——这是 Redis 阻塞原语(BZPOPMIN类操作)的上限,见 worker.py 中关于default_maximum_block_timeout的注释。而 postgres_backend.py 将其覆盖为 3600 秒,理由写得很清楚:PostgreSQL 的LISTEN/NOTIFY让连接保持打开并自动重新武装到下一个到期任务,没有 Redis 那种"为廉价重连而限制 10s"的需求;更大的上限能让空闲 worker 真正安静下来,而不是每 10 秒轮询一次——这对按空闲挂起的 serverless Postgres 尤其重要。此外还设置了minimumBlockTimeout与capabilities(如canBlockFor1Ms、canDoubleTimeout)等能力标记。
worker 侧通过getattr(self.backend, "maximumBlockTimeout", None)探测后端属性,后端没有该属性时回退到默认值(见 worker.py 与 python/tests/worker_disconnect_test.py 中"后端缺失该属性时回退为 10"的测试)。
3.2.0:Queue.getDeduplicationJobId
v3.2.0 为Queue新增getDeduplicationJobId方法,用于根据去重标识反查任务 ID。调用方式:
job_id = await queue.getDeduplicationJobId("my-dedup-id")高层实现位于 python/bullmq/queue.py:它直接把请求转发给后端抽象(return await self.backend.getDeduplicationJobId(id))。两个后端各自的实现:
- Redis 后端:对
{keys['de']}:{id}做一次普通GET——de即 deduplication key 命名空间(见 redis_backend.py,注释明确说明这与 Node.js 客户端的Queue#getDeduplicationJobId对齐); - PostgreSQL 后端:在 postgres_backend.py 中有对应实现,其 SQL 语句见仓库的 PostgreSQL 命令目录(如 python/bullmq/postgres/commands/get_deduplication_job_id.sql)。
配套测试在 python/tests/deduplication_test.py,覆盖了缺失 ID(返回空)、任务完成后去重键被清理(返回空)、共享去重 ID 指向同一任务等多种场景。这个方法的实用价值在于:任务因去重被跳过时,调用方仍能拿到"真正干活的那个任务"的 ID,用于查询状态或关联业务。
其余修复与性能改进一览
- v3.0.5 失败原因存储:worker 存储失败原因时不再做额外 JSON 编码——
moveToFailedArgs中failedReason直接作为参数传给moveToFinished(见 python/bullmq/scripts.py),修复了失败原因被双重编码的问题(对应 issue #4596)。 - v3.0.6 速率限制:处理 deferred failures 时不再计入速率限制窗口——失败任务的处理不再抢占限流配额,这在重试风暴场景下能显著改善吞吐(同步了 elixir/rust/dotnet 的行为)。
- v3.2.3 去重键清理:当任务键已不存在时,删除残留的 deduplication key——修复了去重 ID 长期占用导致新任务被误判为重复的问题(跨 python/elixir/php/rust/dotnet 同步)。
- v3.0.1 发布完整性:补全发布包中缺失的 SQL 文件(影响 Python 与 Elixir 客户端),确保 PostgreSQL 后端开箱即用。
- 依赖维护:v3.0.2 升级 redis 至 v7.4.1,v3.2.1 升级 psycopg 至 v3.3.5,v3.2.2 升级 semver 至 v3.1.0,v3.2.4 ~ v3.2.6 批量更新 Python 依赖(含 virtualenv v21.9.0)。
如何在仓库中跟进后续版本
当前仓库的 Python 客户端实现位于 python/ 目录,除 changelog 外还可参考:
- 顶层导出:python/bullmq/init.py(含
DelayedError、WaitingChildrenError、UnrecoverableError等); - 后端契约与实现:python/bullmq/backend.py、python/bullmq/backends/;
- 高层类:python/bullmq/queue.py、python/bullmq/worker.py、python/bullmq/job.py、python/bullmq/job_scheduler.py、python/bullmq/flow_producer.py;
- Redis Lua 脚本注册表:python/bullmq/scripts.py;
- PostgreSQL SQL 命令:python/bullmq/postgres/commands/;
- 测试:python/tests/(deduplication、delay、job scheduler、postgres backend、worker disconnect 等主题均有覆盖)。
如果使用 pip 安装,可通过pip install bullmq获取发布版本;仓库内的 python/pyproject.toml 与 python/setup.py 定义了包元数据与依赖。本文所有行为描述均以当前仓库源码与 changelog 为准,升级前请以你实际安装的版本对应的发布说明为准。
- 后端
- 消息队列
- 任务调度
【免费下载链接】bullmq
BullMQ - Message Queue and Batch processing for NodeJS, Python, .NET, Elixir, Rust and PHP based on Redis or PostgreSQL
相关推荐
BullMQ Python 客户端 v3 演进解读:可插拔后端架构、去重与延迟控制新能力全览
BullMQ Python 客户端 v3 演进解读:可插拔后端架构、去重与延迟控制新能力全览 导读:本文以 python/CHANGELOG.md 为主体,梳理
后端消息队列任务调度Tcell v2 破坏性变更与新特性解析:LazyGit 终端 UI 底座的演进
Tcell v2 破坏性变更与新特性解析:LazyGit 终端 UI 底座的演进 本文以 CHANGESv2.md https://link.gitcode.c
开发工具CLI版本控制NetBox v3.6 版本全解析:新特性、破坏性变更与 REST API 演进指南
NetBox v3.6 版本全解析:新特性、破坏性变更与 REST API 演进指南 NetBox v3.6 于 2023 年 8 月 30 日正式发布,是 N
后端网络数据建模
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考