前言
最近在工作中,有同事在 PR 中提出一个问题:如果 BullMQ 中的某个 job sleep
1 秒,是否会导致该 worker 中的其他 job 也被阻塞?本文根据这个问题做了一个小实验,证明 BullMQ 中的某个 job 即使 sleep
,也不会阻塞同一个 worker 中的其他 jobs 。(对于BullMq worker, queue, job概念不理解可以看官网)
项目主要依赖
- bullmq: "^5.56.0",
- ioredis: "^5.6.1"
本地启动redis docker服务
css
docker run -d --name redis -p 6379:6379 redis
测试步骤
javascript
import { Queue, Worker } from "bullmq";
import IORedis from "ioredis";
// 初始化 Redis 连接 ioredis默认连接localhost, 6378端口。
const connection = new IORedis({ maxRetriesPerRequest: null });
// 创建一个并发度为 5 的 worker,用于消费 "foo" 队列中的任务
const worker = new Worker(
"foo",
async (job) => {
console.log(`Processing job ${job.name} with data:`, job.data);
if (job.name === "1") {
// 模拟 job 1 为耗时任务(sleep 100ms)
await new Promise((resolve) => setTimeout(resolve, 100));
}
console.log(`${job.name} has completed!`);
},
{ connection, concurrency: 5 }
);
worker.on("completed", (job) => {
job.remove();
console.log(`${job.name} has removed!`);
});
worker.on("failed", (job, err) => {
console.log(`${job.name} has failed with ${err.message}`);
});
const myQueue = new Queue("foo");
await myQueue.add("1", { data: 1 });
await myQueue.add("2", { data: 2 });
await myQueue.add("3", { data: 3 });
await myQueue.add("4", { data: 4 });
setInterval(() => {}, 1000); // 每1s执行一个空函数避免程序退出
从上面代码看出我们在一个worker中创建了4个job,其中对于job.name = 1的job,我们让其sleep了0.1s。执行node ./bull-mq/test.js, 结果如下:
当job 1在等待时,还是会继续执行job 2,3,4。
原理
BullMQ 的 Worker
本质上是通过 Node.js 的事件循环和异步机制来调度任务执行。当设置 concurrency > 1
时,Worker 会并发拉取多个 job 并行执行(通过 Promise 协程机制实现)。即使某个 job 在 await
(如 sleep),也不会阻塞其他 job 的调度和执行。因此,一个 job 的耗时不会影响其他 job 的开始执行。