BullMQ
BullMQ es la librería de colas respaldada por Redis que la mayoría de los equipos de Node utilizan para correos electrónicos, webhooks, procesamiento de medios y sincronización en segundo plano.
Busca en todas las páginas de la documentación
BullMQ es la librería de colas respaldada por Redis que la mayoría de los equipos de Node utilizan para correos electrónicos, webhooks, procesamiento de medios y sincronización en segundo plano.
Tarjeta de receta de referencia rápida - lista para copiar y pegar.
import { Queue, Worker } from "bullmq";
import IORedis from "ioredis";
const connection = new IORedis(process.env.REDIS_URL!, { maxRetriesPerRequest: null });
export const reportQueue = new Queue("reports", { connection });
new Worker(
"reports",
async (job) => {
await buildReport(job.data.reportId);
},
{ connection: connection.duplicate(), concurrency: 3 }
);Cuándo usarlo:
// src/queues/connection.ts
import IORedis from "ioredis";
export function createBullConnection() {
return new IORedis(process.env.REDIS_URL!, {
maxRetriesPerRequest: null,
enableReadyCheck: false,
});
}
// src/queues/reports.ts
import { Queue, Worker, QueueEvents } from "bullmq";
import { createBullConnection } from "./connection";
import { buildReport } from "../services/reports";
const connection = createBullConnection();
export const reportsQueue = new Queue("reports", { connection });
export function startReportWorker() {
const worker = new Worker(
"reports",
async (job) => {
await job.updateProgress(10);
const url = await buildReport(job.data.reportId);
await job.updateProgress(100);
return { url };
},
{
connection: createBullConnection(),
concurrency: 2,
lockDuration: 30_000,
}
);
worker.on("failed", (job, err) => {
console.error({ msg: "job_failed", jobId: job?.id, err: err.message });
});
worker.on("stalled", (jobId) => {
console.warn({ msg: "job_stalled", jobId });
});
return worker;
}
// src/api/reports-route.ts
import express from "express";
import { reportsQueue } from "../queues/reports";
const router = express.Router();
router.post("/", async (req, res) => {
const job = await reportsQueue.add(
"build",
{ reportId: req.body.reportId },
{ attempts: 3, backoff: { type: "exponential", delay: 1000 } }
);
res.status(202).json({ jobId: job.id });
});
export default router;Lo que esto demuestra:
failed y stalled para visibilidad de operacioneslockDuration expireconst worker = startReportWorker();
process.on("SIGTERM", async () => {
await worker.close(); // espera el trabajo actual por defecto
process.exit(0);
});terminationGracePeriodSeconds de k8s > duración del trabajo más largoawait reportsQueue.add(
"nightly",
{},
{ repeat: { pattern: "0 2 * * *" }, jobId: "nightly-report" }
);jobId evita definiciones repetibles duplicadas en el redespliegueimport { FlowProducer } from "bullmq";
const flow = new FlowProducer({ connection });
await flow.add({
name: "invoice-pdf",
queueName: "pdf",
data: { invoiceId },
children: [{ name: "fetch-line-items", queueName: "data", data: { invoiceId } }],
});@bull-board/express detrás de la autenticación de administradormaxRetriesPerRequest por defecto en el worker - rompe BRPOP. Solución: null en las conexiones Bull.stalled.| Alternativa | Usar cuándo | No usar cuándo |
|---|---|---|
| AWS SQS | Sin operaciones de Redis, nativo de AWS | Necesitas flujos de trabajo complejos sin orquestación adicional |
| pg-boss | Pila solo de Postgres | Redis ya es central |
| Temporal | Sagas de larga duración, tareas humanas | Cola de correo electrónico simple |
| RabbitMQ | AMQP empresarial existente | El equipo no tiene experiencia en operaciones de Erlang |
BullMQ es el sucesor mantenido con mejor TypeScript y rendimiento. Los proyectos nuevos usan BullMQ.
Divide por dominio y SLO: email, webhooks, media. Evita una cola gigante.
Comienza con 2-5 trabajos de E/S. Trabajo ligado a la CPU: concurrencia 1 por núcleo después de perfilar.
Compatible con prefijos y etiquetas hash según la documentación de BullMQ. Prueba la conmutación por error.
BullModule.registerQueue en el módulo API; el arranque del worker separado importa los procesadores.
jobId estable en queue.add. Combínalo con claves de idempotencia en el worker.
La opción de limitador de velocidad de BullMQ por cola protege las cuotas del proveedor.
Usa Redis Testcontainer; afirma que el trabajo se completa y el efecto secundario ocurrió una vez.
Instrumentación de OpenTelemetry bullmq + métricas de profundidad de cola (esperando, activo, retrasado).
La alta prioridad inunda la baja - usa colas separadas para SLA escalonados en su lugar.
Versiones de la pila: Esta página fue escrita para Node.js 24.18.0 (LTS activa), npm 10+, TypeScript 5.6+, Express 5, Fastify 5 y NestJS 11.
Revisado por Chris St. John·Última actualización: 16 jul 2026