أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالعروض
أحمد حايس

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

المنصة مبنية على الوضوح، التطبيق، والنتيجة النافعة: شرح مرتب يساعدك تفهم الأدوات، تكتب كودًا أفضل، وتستخدم الذكاء الاصطناعي بوعي داخل العمل الحقيقي.

تعلم أسرعوصول مباشر للدورات والمسارات من الموبايل.
تنقل أوضحالروابط الأساسية والدعم في مكان واحد بدون تشتيت.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • العروض
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع
محتاج مسار سريع؟
ابدأ من الدوراتتواصل معناالأسئلة الشائعة

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالعروضالمدونةالدخول
How To Make It

اعمل Job Queue للإيميلات بـ BullMQ وRedis

📅 ٢٦ أبريل ٢٠٢٦⏱ 5 دقائق قراءة
اعمل Job Queue للإيميلات بـ BullMQ وRedis

اعمل Job Queue للإيميلات بـ BullMQ وRedis

هتقلل انتظار المستخدم من 8 ثواني تقريبًا إلى أقل من نصف ثانية لما تنقل إرسال الإيميل من طلب HTTP إلى Job Queue واضحة وقابلة للقياس.

مستوى القارئ: متوسط

المشكلة باختصار

لو المستخدم ضغط زر تسجيل، والسيرفر بدأ يرسل إيميل الترحيب داخل نفس طلب HTTP، فالطلب كله هيستنى خدمة البريد. لو مزود البريد اتأخر 6 أو 8 ثواني، المستخدم هيشوف تحميل طويل رغم إن التسجيل نفسه خلص.

الطريقة دي بتفشل أكثر لما عندك حملة تسجيل، أو 50K زائر في اليوم، أو مزود بريد بيرفض بعض الطلبات مؤقتًا. بدل ما تجعل تجربة المستخدم رهينة خدمة خارجية، حط المهمة في Queue، ورد على المستخدم فورًا، وخلي Worker ينفذ الإرسال في الخلفية.

رسم معماري يوضح انتقال طلب Node.js API إلى Redis Queue ثم Workers مع retries وfailed jobs

الفكرة بمثال بسيط

ركز في المثال ده: عندك مطعم صغير. الكاشير لا يدخل المطبخ ليطبخ الطلب بنفسه. هو يسجل الطلب بسرعة، يعطي العميل رقمًا، ثم المطبخ ينفذ. الـ API هنا هو الكاشير، وRedis هو دفتر الطلبات، والـ Worker هو المطبخ.

علميًا، الـ Job Queue تفصل استقبال الطلب عن تنفيذ المهمة البطيئة. BullMQ يستخدم Redis كطبقة تخزين وتنسيق للـ jobs. الـ API يضيف job باسم واضح، والـ Worker يسحبها وينفذها. لو المهمة فشلت، تقدر تستخدم attempts وbackoff بدل ما تضيع المهمة أو تعيدها بعشوائية.

الملفات المطلوبة

الافتراض إن عندك Node.js 20، وDocker، وتطبيق صغير يرسل إيميل بعد التسجيل. هنبدأ بمجلد جديد اسمه email-queue-demo.

Bash
mkdir email-queue-demo
cd email-queue-demo
npm init -y
npm install bullmq ioredis

أضف Redis محليًا بملف docker-compose.yml:

YAML
services:
  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"
    command: ["redis-server", "--appendonly", "yes"]

التشغيل:

Bash
docker compose up -d

الـ trade-off هنا واضح: Redis يضيف مكونًا جديدًا لازم تراقبه وتعمل له backup لو jobs مهمة. المكسب إن طلب HTTP يبقى سريع، والمهام البطيئة تبقى قابلة للإعادة والتوسيع.

أضف Job من الـ API

اعمل ملف producer.js. في تطبيق حقيقي، الكود ده يكون داخل endpoint التسجيل بعد حفظ المستخدم في قاعدة البيانات.

JavaScript
const { Queue } = require('bullmq');

const emailQueue = new Queue('emails', {
  connection: { host: '127.0.0.1', port: 6379 }
});

async function main() {
  await emailQueue.add(
    'welcome-email',
    { userId: 42, email: 'user@example.com' },
    {
      attempts: 5,
      backoff: { type: 'exponential', delay: 1000 },
      removeOnComplete: 1000,
      removeOnFail: 5000
    }
  );

  console.log('job queued, respond to user now');
  await emailQueue.close();
}

main().catch(console.error);

بالظبط كده، الـ API لا يرسل الإيميل الآن. هو فقط يسجل المهمة. لو مزود البريد وقع دقيقة، المستخدم لا ينتظر دقيقة. المهمة ستفشل ثم تعاد بمحاولات تدريجية.

شغل Worker منفصل

اعمل ملف worker.js. هنا هنحاكي إرسال الإيميل بزمن انتظار 2 ثانية. استبدل الدالة لاحقًا بـ SendGrid أو SES أو Resend.

JavaScript
const { Worker } = require('bullmq');

async function sendEmail(job) {
  console.log('sending email to', job.data.email);
  await new Promise(resolve => setTimeout(resolve, 2000));
  console.log('sent job', job.id);
}

const worker = new Worker('emails', sendEmail, {
  connection: { host: '127.0.0.1', port: 6379 },
  concurrency: 5
});

worker.on('completed', job => {
  console.log(`completed ${job.id}`);
});

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

شغل worker في terminal، والـ producer في terminal تاني:

Bash
node worker.js
node producer.js

لو عندك 100 عملية تسجيل في دقيقة، تقدر تزود concurrency إلى 10 أو تشغل أكثر من Worker process. المكسب throughput أعلى. التكلفة ضغط أكبر على مزود البريد وRedis، فلا تزود الرقم بدون rate limit.

القياس المتوقع

في سيناريو واقعي، إرسال إيميل عبر API خارجي قد يأخذ 2 إلى 8 ثواني حسب الشبكة ومزود الخدمة. بعد نقل المهمة إلى Queue، طلب التسجيل غالبًا يرجع في 200 إلى 500ms لأنه لم يعد ينتظر الإرسال.

رسم أعمدة يقارن زمن انتظار المستخدم عند إرسال الإيميل داخل HTTP مقابل BullMQ Redis Queue

الرقم هنا ليس وعدًا مطلقًا. هو قياس تقريبي لتطبيق صغير: قبل النقل 8.5 ثانية لأن الطلب ينتظر مزود البريد، وبعد النقل 0.45 ثانية لأن الطلب يضيف job فقط. أفضل طريقة عندك هي قياس p95 قبل وبعد.

ما الذي يجب الانتباه له

  • Idempotency: لا ترسل نفس الإيميل مرتين لو job اتعاد. استخدم مفتاح مثل welcome:userId أو سجل حالة الإرسال في قاعدة البيانات.
  • المراقبة: راقب عدد jobs الفاشلة والمتأخرة. Queue بدون dashboard أو alert تتحول لمكان تخبئ فيه الأخطاء.
  • الأولوية: لا تخلط إيميلات الترحيب مع فواتير الدفع في نفس queue لو الفواتير أهم.
  • إغلاق worker: في الإنتاج، تعامل مع SIGTERM حتى لا تقطع job في منتصف الإرسال أثناء deploy.

الـ trade-off النهائي: أنت تكسب سرعة وتجربة مستخدم أفضل وإعادة محاولات منظمة. وتخسر بساطة التشغيل، لأن عندك Redis وWorker ومراقبة إضافية.

متى لا تستخدم هذه الطريقة

لا تستخدم BullMQ لو المهمة لازم تنتهي قبل رد HTTP، مثل التحقق الفوري من كود دفع داخل نفس العملية. ولا تستخدمه لو عندك مشروع صغير جدًا يرسل 5 إيميلات في اليوم وتأخير الإرسال لا يضر المستخدم. في الحالة دي، التعقيد أعلى من المكسب.

كذلك لا تعتمد على Redis محلي بدون persistence لو فقدان jobs يسبب مشكلة مالية أو قانونية. استخدم إعداد Redis موثوق، أو خدمة managed، أو Queue مخصصة مثل SQS لو فريقك يعمل أصلًا على AWS.

مصادر اعتمدت عليها

  • BullMQ Queues documentation
  • BullMQ retrying failing jobs
  • Docker Compose services reference
  • Redis data structures documentation

الخطوة التالية

افتح أبطأ endpoint عندك، واكتب بجانبه قائمة بالمهام التي لا يحتاجها المستخدم قبل الرد. اختر مهمة واحدة فقط، غالبًا إرسال إيميل أو توليد PDF، وانقلها إلى BullMQ بنفس النمط السابق ثم قارن p95 قبل وبعد.

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة