العودة إلى المدونة
برمجيات خاصة

ما هي قائمة انتظار الرسائل؟ المهام الخلفية مع RabbitMQ

ما هي قائمة انتظار الرسائل وما فائدتها؟ المنتج والمستهلك، نقل البريد والفواتير والإشعارات إلى الخلفية باستخدام RabbitMQ، التأكيد، إعادة المحاولة، قائمة الرسائل الميتة، عدم التكرار (idempotency) ومتى لا تكون القائمة ضرورية.

Mesaj KuyruğuRabbitMQBackendMimari

قائمة انتظار الرسائل (message queue) طبقة وسيطة يترك فيها التطبيق المهمة على شكل رسالة بدل تنفيذها فورًا، ثم تلتقط عملية أخرى هذه الرسائل بالترتيب وتعالجها. الإجابة المختصرة: تفصل عن طلب الويب الأعمال التي لا ينبغي أن تُبقي المستخدم منتظرًا أو التي تعتمد على نظام خارجي (البريد، إصدار الفواتير، الإشعارات، إنشاء التقارير)، وإذا تباطأ النظام الخارجي أو توقف لا تضيع المهمة بل تنتظر في القائمة. ويُعد RabbitMQ من أكثر الأدوات مفتوحة المصدر استخدامًا لهذا الغرض.

ما هي قائمة انتظار الرسائل وكيف تعمل؟

يتكون النموذج من ثلاثة أجزاء. المنتج (producer) يرسل رسالة صغيرة تصف العمل، مثل «أصدر فاتورة للطلب رقم 42». والوسيط (broker)، وهو هنا RabbitMQ، يحفظ الرسالة بشكل دائم. والمستهلك (consumer) عملية عاملة منفصلة تأخذ الرسالة وتنفذ العمل ثم تؤكد (ack) انتهاءه. وإذا تعطل العامل قبل التأكيد يسلّم الوسيط الرسالة إلى عامل آخر. وبذلك يحفظ خادم الويب الطلب ويرد على المستخدم فورًا، بينما تُصدر الفاتورة بعد ثوانٍ في الخلفية.

فيمَ تُستخدم قائمة انتظار الرسائل؟

  • إرسال البريد والرسائل القصيرة: شاشات التسجيل وإتمام الطلب لا تنتظر حتى لو كان المزود بطيئًا.
  • الفواتير والتكاملات الرسمية: في الخدمات الخارجية التي قد يتأخر ردها، مثل تكامل الفاتورة الإلكترونية، تتولى القائمة إعادة المحاولة.
  • أحداث webhook الواردة: يُقبل الحدث فورًا ويوضع في القائمة ثم يُعالج منفصلًا، والتفاصيل في مقال ما هو الـ webhook.
  • الأعمال الثقيلة: إنشاء ملفات PDF والتقارير، وتصغير الصور، والاستيراد الجماعي.
  • التواصل بين الخدمات: عندما يجب أن تتفاعل عدة خدمات مع حدث في خدمة واحدة.
القائمة تضمن ألا يضيع العمل، لكنها لا تضمن تنفيذه مرة واحدة فقط. قد تصل الرسالة نفسها أكثر من مرة، لذا يجب أن يكون كل مستهلك غير متأثر بالتكرار: استلام المهمة نفسها مرة ثانية يجب ألا يغيّر النتيجة.

التأكيد وإعادة المحاولة وقائمة الرسائل الميتة

في الإعداد المتين تُتخذ ثلاثة قرارات من البداية. الأول التأكيد: يؤكد المستهلك الرسالة بعد انتهاء العمل فعلًا لا عند استلامها. الثاني إعادة المحاولة: عند الأخطاء المؤقتة تُعاد الرسالة عددًا محدودًا من المرات مع فترة انتظار، دون حلقة لا نهائية. الثالث قائمة الرسائل الميتة (dead letter queue): الرسالة التي استنفدت محاولاتها تنتقل إلى قائمة منفصلة تُراقب فيها، وتُعاد يدويًا بعد إصلاح السبب. ويجب مراقبة عدد الرسائل المتراكمة هناك وإطلاق تنبيه عند تجاوز حد معين.

يوضح مثال Node.js التالي منتجًا يضع الفاتورة في القائمة بعد الطلب، ومستهلكًا يعالجها. معرّف الرسالة هو رقم الطلب، والدالة createInvoiceOnce في المستهلك لا تصدر فاتورة ثانية للطلب نفسه أبدًا.

import amqp from 'amqplib';

const conn = await amqp.connect(process.env.AMQP_URL ?? 'amqp://localhost');
const ch = await conn.createChannel();
await ch.assertQueue('invoices', { durable: true });

// Producer: the web request only drops a message and returns.
export function enqueueInvoice(orderId: string) {
  ch.sendToQueue('invoices', Buffer.from(JSON.stringify({ orderId })), {
    persistent: true,
    messageId: orderId, // used for idempotency on the consumer side
  });
}

// Consumer: a separate worker process.
await ch.prefetch(5);
await ch.consume('invoices', async (msg) => {
  if (!msg) return;
  const { orderId } = JSON.parse(msg.content.toString());
  try {
    await createInvoiceOnce(orderId); // safe to run twice for the same order
    ch.ack(msg);
  } catch {
    ch.nack(msg, false, false); // goes to the dead letter queue
  }
});

RabbitMQ أم Redis أم Kafka؟

صُمم RabbitMQ لقوائم المهام التقليدية: التوجيه والتأكيد والأولويات وقوائم الرسائل الميتة متوفرة مسبقًا. وإذا كان التطبيق يستخدم Redis أصلًا، فقد تكفي مكتبات القوائم المبنية على Redis للمهام الخلفية منخفضة الحجم، ولاستخداماته الأخرى راجع مقال ما هو Redis. أما Kafka فسجل أحداث يحتفظ بالرسائل مدة طويلة، ويناسب تدفقات الأحداث الكبيرة والحاجة إلى إعادة قراءة البيانات لاحقًا، وهو أكثر من اللازم لقائمة مهام بسيطة. ما يحدد الاختيار هو مدة حفظ الرسالة وعدد القرّاء، لا شهرة الأداة.

متى لا تكون قائمة الانتظار ضرورية؟

ليست كل مهمة خلفية بحاجة إلى قائمة. للتقارير أو أعمال التنظيف في أوقات ثابتة تكون المهمة المجدولة (cron job) أبسط. وفي تطبيق قليل الزيارات على خادم واحد يكفي غالبًا جدول «مهام معلقة» في قاعدة البيانات وعامل صغير يقرؤه. فالقائمة مكون إضافي يحتاج إلى مراقبة ونسخ احتياطي وتحديث. وللسبب نفسه من المهم الإجابة عن سؤال الخدمات المصغرة أم التطبيق الأحادي قبل تقسيم التطبيق إلى خدمات.

الخلاصة

قائمة انتظار الرسائل تنقل بأمان إلى الخلفية الأعمال التي لا ينبغي أن تُبقي المستخدم منتظرًا أو التي تعتمد على أنظمة خارجية. وتظهر قيمتها عندما يُعدّ التأكيد وإعادة المحاولة المحدودة وقائمة الرسائل الميتة والمستهلك غير المتأثر بالتكرار معًا. إذا أردت تخطيط المعالجة الخلفية في مشروعك معنا، فاطّلع على صفحة البرمجيات الخاصة أو راسلنا عبر نموذج طلب عرض سعر.

Let's Build Your Project

Get a free consultation for your website, mobile app, or corporate software project.

Get a Free QuoteExplore our Custom Software service