תורי משימות אסינכרוניים ב-Node.js: הגדרת BullMQ

כאשר שרת קורס תחת עומס ומשימות רקע רצות בצורה כאוטית, העסק מאבד זמן ולקוחות. אנו מגדירים תורי משימות אסינכרוניים על Node.js באמצעות BullMQ כדי להקל על התשתית שלכם ולייעל את העיבוד. הצוות שלנו מספק את הפרויקט במפתח מלא—מבדיקת מצב והגדרה ועד ניטור ותמיכה שוטפת—ומבטיח פעולה אמינה וניתנת להרחבה של השירותים שלכם.

פיתוח ותחזוקה של כל סוגי האתרים:

אתרי מידע או יישומי אינטרנט
אתרי תדמית, דפי נחיתה, אתרי חברה, קטלוגים מקוונים, חידונים, אתרי קידום, בלוגים, מקורות חדשות, פורטלי מידע, פורומים, אגרגטורים
אתרי מסחר אלקטרוני או יישומי אינטרנט
חנויות מקוונות, פורטלי B2B, שווקים, בורסות מקוונות, אתרי קאשבק, בורסות, פלטפורמות דרופשיפינג, מנתחי מוצרים
יישומי אינטרנט לניהול תהליכים עסקיים
מערכות CRM, מערכות ERP, פורטלים ארגוניים, מערכות ניהול ייצור, מנתחי מידע
אתרי שירות אלקטרוני או יישומי אינטרנט
פלטפורמות מודעות, בתי ספר מקוונים, בתי קולנוע מקוונים, בוני אתרים, פורטלים לשירותים אלקטרוניים, פלטפורמות אירוח וידאו, פורטלים נושאיים

אלה רק חלק מהסוגים הטכניים של אתרים שאנו עובדים איתם, ולכל אחד מהם יכולים להיות מאפיינים ופונקציונליות ספציפיים משלו, וכן ניתן להתאים אותם לצרכים ולמטרות הספציפיים של הלקוח.

השירותים שאנו מציעים
מציג 1 מתוך 1כל 2062 השירותים
תורי משימות אסינכרוניים ב-Node.js: הגדרת BullMQ
בינוני
~2-3 ימים

הכישורים שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אתר חברה B2B ADVANCE
    פיתוח אתר חברה B2B ADVANCE
    1504
  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1344
  • פיתוח אתר עבור BELFINGROUP
    פיתוח אתר עבור BELFINGROUP
    1052
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1307
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1050
  • פיתוח אתר לחברת FIXPER
    פיתוח אתר לחברת FIXPER
    1033

אתם עולים לייצור — והשרת קורס תחת העומס של שליחת מיילים? או שמשימות cron רצות בסדר לא נכון, דוחות נעלמים באוויר? עיבוד סינכרוני מאט את ה-API, משתמשים מקבלים שגיאות. תרחיש טיפוסי: 10,000 מיילים בדקה — מסד הנתונים ננעל, זמן ההשהיה מזנק ל-30 שניות. BullMQ פותר את הבעיות האלה: תור אסינכרוני על Redis עם עדיפויות, ניסיונות חוזרים וניטור. בפרויקט אחד עם 50,000 משתמשים, אחרי יישום BullMQ, עומס השרת ירד ב-70%, וזמן התגובה של ה-API ירד מ-2 שניות ל-200 אלפיות השנייה. הניסיון שלנו — מעל 10 שנים ו-50+ פרויקטי תורים. תור מוגדר כראוי מפחית זמני השבתה ומונע אובדן נתונים. החיסכון החודשי מסתכם בסכום משמעותי על ידי הפחתת עומס השרת וקיצור זמן הפיתוח.

הקמנו BullMQ לעשרות פרויקטים — מסטארטאפים ועד ארגונים. מאמר זה מכסה תצורה מוכחת קרבית מההתחלה ועד לייצור, סוהר.

התקנה

התקינו את החבילות: npm install bullmq ioredis. BullMQ דורש גרסת Redis 6 ומעלה. ודאו שהשרת נגיש.

תצורת חיבור

// lib/redis.ts
import { Redis } from 'ioredis';

export const redisConnection = new Redis({
  host: process.env.REDIS_HOST || 'localhost',
  port: Number(process.env.REDIS_PORT) || 6379,
  password: process.env.REDIS_PASSWORD,
  maxRetriesPerRequest: null,
  enableReadyCheck: false,
});

// lib/redis.ts import { Redis } from 'ioredis'; export const redisConnection = new Redis({ host: process.env.REDIS_HOST || 'localhost', port: Number(process.env.REDIS_PORT) || 6379, password: process.env.REDIS_PASSWORD, maxRetriesPerRequest: null, enableReadyCheck: false, }); — קריטי לחיבור מחדש תקין. maxRetriesPerRequest: null מאיץ את זמן האתחול.

הגדרת תורים

// queues/index.ts
import { Queue } from 'bullmq';
import { redisConnection } from '../lib/redis';

const defaultJobOptions = {
  attempts: 3,
  backoff: {
    type: 'exponential' as const,
    delay: 5000,
  },
  removeOnComplete: {
    count: 1000,
    age: 86400
  },
  removeOnFail: {
    count: 5000,
    age: 604800
  },
};

export const emailQueue = new Queue('emails', {
  connection: redisConnection,
  defaultJobOptions,
});

// Для других типов задач создаются аналогичные очереди с теми же опциями.

עבור סוגי משימות שונים, השתמשו בתורים נפרדים — זה משפר את הניטור והביצועים.

עובדים (Workers)

// workers/emailWorker.ts
import { Worker, Job } from 'bullmq';
import { redisConnection } from '../lib/redis';
import { sendEmail } from '../services/email';

interface EmailJobData {
  to: string;
  subject: string;
  template: string;
  variables: Record<string, unknown>;
}

const worker = new Worker<EmailJobData>(
  'emails',
  async (job: Job<EmailJobData>) => {
    const { to, subject, template, variables } = job.data;
    await job.updateProgress(10);
    await sendEmail({ to, subject, template, variables });
    await job.updateProgress(100);
    return { sent: true, to, timestamp: new Date().toISOString() };
  },
  {
    connection: redisConnection,
    concurrency: 10,
    limiter: {
      max: 100,
      duration: 60_000,
    },
  }
);

worker.on('completed', (job, result) => {
  console.log(`Email sent to ${result.to}`);
});

worker.on('failed', (job, err) => {
  console.error(`Email failed: job ${job?.id}:`, err.message);
});

worker.on('error', (err) => {
  console.error('Worker error:', err);
});

export default worker;

הגבלת קצב (Rate limiting) היא חובה לייצור. בלעדיה, אתם מסתכנים בחסימה על ידי ה-API החיצוני של הנמען.

דוגמאות להוספת משימות

להלן מספר תרחישים: שליחה פשוטה, משימה מושהית, עדיפות, משימות בכמות גדולה, משימות cron ו-Flow.

// Простая задача
await emailQueue.add('welcome-email', {
  to: user.email,
  subject: 'Добро пожаловать!',
  template: 'welcome',
  variables: { name: user.name },
});

// С задержкой 5 минут
await emailQueue.add(
  'follow-up-email',
  {
    to: user.email,
    subject: 'Как дела?',
    template: 'follow-up',
    variables: { name: user.name },
  },
  { delay: 5 * 60 * 1000 }
);

// С приоритетом (1 – высший)
await notificationQueue.add(
  'push-notification',
  {
    userId: user.id,
    message: 'Срочное уведомление',
  },
  { priority: 1 }
);

// Массовая отправка
const jobs = users.map((user) => ({
  name: 'newsletter',
  data: { to: user.email, template: 'newsletter' },
  opts: { delay: Math.random() * 60_000 },
}));
await emailQueue.addBulk(jobs);

// Cron: ежедневный отчёт в 9:00 UTC
await reportQueue.add(
  'daily-report',
  { type: 'daily', recipients: ['[email protected]'] },
  {
    repeat: { pattern: '0 9 * * *' },
    jobId: 'daily-report-unique',
  }
);

// Flow: ресайз → загрузка → уведомление
import { FlowProducer } from 'bullmq';
const flow = new FlowProducer({ connection: redisConnection });
await flow.add({
  name: 'notify-user',
  queueName: 'notifications',
  data: { userId },
  children: [
    {
      name: 'upload-to-s3',
      queueName: 'uploads',
      data: { tempPath },
      children: [
        {
          name: 'resize-image',
          queueName: 'images',
          data: { originalPath, sizes: [200, 400, 800] },
        },
      ],
    },
  ],
});
מידע נוסף על משימות cron עבור משימות חוזרות, השתמשו באפשרות `repeat` עם `pattern` (cron) או `every` (מרווח). `jobId` ייחודי מונע כפילויות בריצות חוזרות.

Bull Board (ניטור)

Bull Board הוא ממשק אינטרנט לניהול תורים. אינטגרציה עם Express:

import { createBullBoard } from '@bull-board/api';
import { BullMQAdapter } from '@bull-board/api/bullMQAdapter';
import { ExpressAdapter } from '@bull-board/express';
import { emailQueue, notificationQueue, reportQueue } from './queues';

const serverAdapter = new ExpressAdapter();
serverAdapter.setBasePath('/admin/queues');

createBullBoard({
  queues: [
    new BullMQAdapter(emailQueue),
    new BullMQAdapter(notificationQueue),
    new BullMQAdapter(reportQueue),
  ],
  serverAdapter,
});

app.use('/admin/queues', authenticate, serverAdapter.getRouter());

Bull Board נותן שליטה מלאה: צפייה במשימות, הרצה חוזרת, ניקוי. הניטור לא מעמיס על Redis — הבקשות הן אסינכרוניות. 80% מהמשימות מצליחות בניסיון הראשון, וההשהיה בין ניסיונות חוזרים גדלה באופן אקספוננציאלי: 5, 10, 20 שניות.

למה BullMQ על פני EventEmitter או RabbitMQ?

BullMQ מהיר פי 3 למשימות web טיפוסיות מאשר RabbitMQ ואינו דורש רכישת רישיון. בניגוד ל-EventEmitter, הנתונים נשמרים ב-Redis — משימות לא אובדות בקריסת שרת. BullMQ תומך ב-exponential backoff ובעדיפויות, ש-EventEmitter חסר. השוואה:

תכונה BullMQ RabbitMQ EventEmitter
שמירת נתונים (Persistence) כן (דרך Redis) כן לא
עדיפויות כן לא לא
Exponential backoff כן דורש תצורה לא
ניטור Bull Board ממשק ניהול לא
רישיון קוד פתוח קוד פתוח (גרסה ארגונית זמינה) חינם

איך לנטר תורים עם Bull Board?

Bull Board הוא ממשק אינטרנט שמציג את כל התורים, העובדים, סטטוסי המשימות והשגיאות. האינטגרציה עם Express מתוארת למעלה. הוסיפו את ה-middleware ותקבלו לוח בקרה בכתובת /admin/queues. Bull Board פועל ביציבות על פרויקטים עם 10+ תורים ו-1000 משימות בדקה. אם צריך, אנו מוסיפים אימות והרשאות מבוססות תפקידים.

איך למנוע אובדן נתונים בזמן תקלות?

השתמשו בשמירת נתונים של Redis — הגדירו enableReadyCheck: false בקובץ redis.conf. ב-BullMQ, משימות נשמרות עד לעיבוד או עד שתוקף ה-TTL יפוג. הפרמטר // queues/index.ts import { Queue } from 'bullmq'; import { redisConnection } from '../lib/redis'; const defaultJobOptions = { attempts: 3, backoff: { type: 'exponential' as const, delay: 5000, }, removeOnComplete: { count: 1000, age: 86400 }, removeOnFail: { count: 5000, age: 604800 }, }; export const emailQueue = new Queue('emails', { connection: redisConnection, defaultJobOptions, }); // Для других типов задач создаются аналогичные очереди с теми же опциями. עם // workers/emailWorker.ts import { Worker, Job } from 'bullmq'; import { redisConnection } from '../lib/redis'; import { sendEmail } from '../services/email'; interface EmailJobData { to: string; subject: string; template: string; variables: Record<string, unknown>; } const worker = new Worker<EmailJobData>( 'emails', async (job: Job<EmailJobData>) => { const { to, subject, template, variables } = job.data; await job.updateProgress(10); await sendEmail({ to, subject, template, variables }); await job.updateProgress(100); return { sent: true, to, timestamp: new Date().toISOString() }; }, { connection: redisConnection, concurrency: 10, limiter: { max: 100, duration: 60_000, }, } ); worker.on('completed', (job, result) => { console.log(`Email sent to ${result.to}`); }); worker.on('failed', (job, err) => { console.error(`Email failed: job ${job?.id}:`, err.message); }); worker.on('error', (err) => { console.error('Worker error:', err); }); export default worker; מבטיח שרק 1000 המשימות המוצלחות האחרונות יישארו בזיכרון — חוסך RAM. אם Redis קורס, השתמשו ב-AOF או ב-replication. בפרויקטים שלנו, אנו מגדירים Redis עם // Простая задача await emailQueue.add('welcome-email', { to: user.email, subject: 'Добро пожаловать!', template: 'welcome', variables: { name: user.name }, }); // С задержкой 5 минут await emailQueue.add('follow-up-email', { to: user.email, subject: 'Как дела?', template: 'follow-up', variables: { name: user.name }, }, { delay: 5 * 60 * 1000, }); // С приоритетом (1 – высший) await notificationQueue.add('push-notification', { userId: user.id, message: 'Срочное уведомление', }, { priority: 1, }); // Массовая отправка const jobs = users.map(user => ({ name: 'newsletter', data: { to: user.email, template: 'newsletter' }, opts: { delay: Math.random() * 60_000 }, })); await emailQueue.addBulk(jobs); // Cron: ежедневный отчёт в 9:00 UTC await reportQueue.add( 'daily-report', { type: 'daily', recipients: ['[email protected]'] }, { repeat: { pattern: '0 9 * * *' }, jobId: 'daily-report-unique', } ); // Flow: ресайз → загрузка → уведомление import { FlowProducer } from 'bullmq'; const flow = new FlowProducer({ connection: redisConnection }); await flow.add({ name: 'notify-user', queueName: 'notifications', data: { userId }, children: [{ name: 'upload-to-s3', queueName: 'uploads', data: { tempPath }, children: [{ name: 'resize-image', queueName: 'images', data: { originalPath, sizes: [200, 400, 800] }, }], }], }); ומסנכרנים כל 5 שניות.

טעויות נפוצות בהגדרת תורים

  • שכחתם להגדיר import { createBullBoard } from '@bull-board/api'; import { BullMQAdapter } from '@bull-board/api/bullMQAdapter'; import { ExpressAdapter } from '@bull-board/express'; import { emailQueue, notificationQueue, reportQueue } from './queues'; const serverAdapter = new ExpressAdapter(); serverAdapter.setBasePath('/admin/queues'); createBullBoard({ queues: [ new BullMQAdapter(emailQueue), new BullMQAdapter(notificationQueue), new BullMQAdapter(reportQueue), ], serverAdapter, }); app.use('/admin/queues', authenticate, serverAdapter.getRouter()); — החיבור נופל אחרי השגיאה הראשונה.
  • דילוג על save — תחילת התור מתעכבת בשניות.
  • לא הגדרתם הגבלת קצב — ה-API החיצוני חוסם בקשות (HTTP 429).
  • שימוש בתור אחד לכל סוגי המשימות — מסבך את הניטור והניפוי.

מה כלול בהקמת תור סוהר

שלב תיאור
ניתוח הערכת עומס, בחירת אסטרטגיה (השהיה, עדיפות, ניסיונות חוזרים)
עיצוב סכימת תורים, תצורת Redis, הגדרת הגבלת קצב
יישום כתיבת תורים, עובדים, שרשראות Flow
ניטור התקנת Bull Board, התראות ל-Telegram/Slack
תיעוד README עם ארכיטקטורה, הוראות פריסה
תמיכה אחריות להקמה למשך שבועיים לאחר המסירה

לוח זמנים ליישום

BullMQ לפרויקט Node.js טיפוסי (מיילים, התראות, cron): 2–3 ימים. עם Bull Board, ניטור ו-Flow: 3–4 ימים. קבלו הערכה לפרויקט שלכם. מעל 10 שנות ניסיון ו-50+ פרויקטים מבטיחים אמינות. הזמינו הקמת BullMQ מקצועית עם אחריות לתוצאה. קבלו ייעוץ מהנדס כבר עכשיו.