تصميم نظام Notification Priority: إرسال الرسائل العاجلة أولاً باستخدام RabbitMQ

تصميم نظام Notification Priority: إرسال الرسائل العاجلة أولاً باستخدام RabbitMQ

في أنظمة الإشعارات الكبيرة (Notifications System)، لا تكون كل الرسائل بنفس الأهمية. إشعار عن محاولة تسجيل دخول مشبوه، أو انقطاع خدمة، يجب أن يصل فورًا، بينما تقرير أسبوعي أو بريد تسويقي يمكن أن ينتظر. هنا تظهر أهمية استخدام rabbitmq priority queue notifications لتحديد أولوية الرسائل وضمان وصول الأهم أولًا.

في هذا المقال سنشرح كيف تستخدم Priority Queues في RabbitMQ لتصميم نظام إشعارات يرسل الرسائل العاجلة قبل العادية، مع أمثلة عملية وأفضل الممارسات، وكيفية دمجه مع تصميمات أنظمة الإشعارات التي شرحناها في مقالات سابقة مثل:

لماذا نحتاج Priority Queues في نظام الإشعارات؟

في نظام إشعارات حقيقي يخدم آلاف أو ملايين المستخدمين، قد تكون لديك أنواع مختلفة من الرسائل:

  • إشعارات حرجة (Critical): محاولات اختراق، توقف نظام، خطأ في الدفع، تجاوز حد معين في النظام.
  • إشعارات مهمة (High): تحديثات أمنية، تنبيهات مهمة من التطبيق، موافقة/رفض طلب.
  • إشعارات عادية (Normal): رسائل تسويقية، نشرات، تحديثات عامة.

إذا أرسلت كل هذه الرسائل في Queue واحدة بدون أولوية، قد تتكدس الرسائل العادية في البداية، وتنتظر الرسائل الحرجة دورها، خاصة عند الضغط العالي. النتيجة: تجربة مستخدم سيئة وتأخر في تنبيهات حرجة.

هنا يأتي دور rabbitmq priority queue notifications، حيث يمكنك:

  • تحديد درجة أولوية لكل رسالة (مثلاً من 0 إلى 10).
  • ضمان أن المستهلكين (Consumers) يسحبون الرسائل الأعلى أولوية أولًا.
  • التحكم في سلوك النظام تحت ضغط عالي بدون الحاجة إلى بناء عشرات الـ Queues المنفصلة.

مفهوم Priority Queue في RabbitMQ

في RabbitMQ، الـ Priority Queue ليست نوع Queue منفصل، بل هي Queue عادية مع خاصية إضافية: x-max-priority. هذه الخاصية تخبر RabbitMQ أن هذه الـ Queue تدعم الأولويات حتى رقم معين.

عند نشر رسالة (Publish)، يمكنك تحديد خاصية priority في Message Properties، وستقوم الـ Queue بترتيب الرسائل داخلها بحيث:

  • الرسالة ذات priority أعلى يتم استهلاكها قبل الأخرى ذات الـ priority الأقل.
  • إذا كان هناك رسائل بنفس الـ priority، يتم استخدام الترتيب العادي (FIFO) بينهم.

مثال: إذا كانت الـ Queue تدعم أولوية من 0 إلى 10، ورسالتان داخل الـ Queue بأولوية 2 و 8، فستخرج أولوية 8 أولًا، حتى لو نُشرت بعد رسالة 2.

تصميم نظام إشعارات مبني على Priority

لنصمم نظام Notification Priority يعتمد على RabbitMQ لإرسال الرسائل العاجلة أولًا. الفكرة العامة كالتالي:

  1. Notification Producer: خدمة أو ميكروسيرفس مسؤولة عن توليد الإشعارات وارسالها إلى RabbitMQ.
  2. Priority Queue: Queue واحدة (أو أكثر) تستقبل جميع أنواع الإشعارات مع خصائص أولوية.
  3. Notification Consumers: خدمات تقوم بسحب الرسائل من الـ Queue وإرسالها عبر Email / Push / SMS / WebSocket.

يمكنك ربط هذا التصميم مع أنظمة أكثر شمولاً كـ نظام إشعارات Real-Time باستخدام RabbitMQ وWebSockets أو مع Push Notifications للتطبيقات باستخدام Firebase.

تحديد مستويات الأولوية

أول خطوة أهم شيء أن تكون Levels of Priority واضحة وثابتة في النظام. مثال عملي:

  • Priority 9 - 10: رسائل حرجة (Critical)
  • Priority 6 - 8: رسائل High
  • Priority 3 - 5: رسائل عادية
  • Priority 0 - 2: رسائل Low أو Background

يمكنك تخزين هذا الماب في Config أو Enum داخل الكود، مثلاً:

// Pseudo-code
enum NotificationPriority {
  CRITICAL = 10,
  HIGH = 7,
  NORMAL = 5,
  LOW = 1
}

إنشاء Priority Queue في RabbitMQ

عند تعريف الـ Queue، يجب إضافة خاصية x-max-priority في الـ arguments.

مثال (Java / Spring AMQP):

Map<String, Object> args = new HashMap<>();
args.put("x-max-priority", 10); // أعلى أولوية مدعومة

Queue notificationQueue = new Queue(
  "notifications.priority",
  true,   // durable
  false,  // exclusive
  false,  // autoDelete
  args
);

مثال (Node.js / amqplib):

await channel.assertQueue("notifications.priority", {
  durable: true,
  arguments: {
    "x-max-priority": 10
  }
});

الآن هذه الـ Queue قادرة على استقبال رسائل بأولوية من 0 حتى 10 (أو أقل، حسب ما حددت).

نشر الرسائل مع أولوية

عند إرسال الإشعار، يقوم الـ Producer بتحديد الأولوية بناءً على نوع الإشعار:

// مثال Node.js
const notification = {
  userId: 123,
  type: "LOGIN_SUSPICIOUS",
  message: "تم رصد محاولة تسجيل دخول مشبوهة",
};

const priority = 10; // Critical

channel.sendToQueue(
  "notifications.priority",
  Buffer.from(JSON.stringify(notification)),
  {
    persistent: true,
    priority: priority
  }
);

مثال آخر لإشعار عادي:

const newsletterNotification = {
  userId: 123,
  type: "NEWSLETTER",
  message: "النشرة الأسبوعية",
};

const priority = 3; // Normal

channel.sendToQueue(
  "notifications.priority",
  Buffer.from(JSON.stringify(newsletterNotification)),
  {
    persistent: true,
    priority: priority
  }
);

بهذا الشكل، في نفس الـ Queue، ستتقدم رسائل LOGIN_SUSPICIOUS على رسائل NEWSLETTER عند استهلاكها.

تصميم الـ Consumer لمعالجة الإشعارات حسب الأولوية

المميز في rabbitmq priority queue notifications أنك لا تحتاج لتعديل كبير في الـ Consumer. الـ Queue نفسها هي من ترتب الرسائل.

خطوات أساسية للـ Consumer:

  1. الاتصال بالـ Queue notifications.priority.
  2. سحب الرسائل (consume).
  3. تحديد نوع الإشعار من الـ payload.
  4. إرسال الإشعار عبر القناة المناسبة (Email / Push / SMS / WebSocket).
// مثال Node.js Consumer
channel.consume("notifications.priority", async (msg) => {
  if (!msg) return;

  const payload = JSON.parse(msg.content.toString());

  try {
    await handleNotification(payload); // ترسل الإشعار
    channel.ack(msg);
  } catch (err) {
    // يمكنك هنا استخدام DLQ كما شرحنا في:
    // /dead-letter-queue-kafka-rabbitmq
    channel.nack(msg, false, false); // إرسال للـ DLQ
  }
}, {
  noAck: false
});

بما أن الـ Queue مرتبة داخليًا بالأولوية، كل ما عليك هو استهلاك الرسائل بالترتيب. ستخرج الرسائل الحرجة أولًا تلقائيًا.

Priority Queues أم Multiple Queues؟

من الأسئلة الشائعة: هل الأفضل تصميم:

  • Queue واحدة مع Priority؟
  • أم عدة Queues مثل:
    • notifications.critical
    • notifications.high
    • notifications.normal

مزايا استخدام Priority Queue واحدة:

  • تصميم أبسط: منتج واحد، Consumer واحد أو مجموعة Consumers.
  • لا تحتاج لتوزيع الـ Workers يدوياً على أكثر من Queue.
  • سهل التوسع أفقياً (زيادة عدد الـ Consumers).

مزايا استخدام عدة Queues:

  • يمكنك تخصيص عدد Workers مختلف لكل نوع (مثلاً: 80% من الـ Workers على critical).
  • سهولة تطبيق سياسات مختلفة لـ TTL أو DLQ لكل Queue.

في أنظمة الإشعارات، غالباً Priority Queue واحدة» خيار ممتاز، خاصة إذا كنت تريد سلوك بسيط: رسائل أكثر أهمية تعالج أولًا، بدون إدارة معقدة لـ Workers.

أفضل الممارسات عند استخدام rabbitmq priority queue notifications

1. لا تبالغ في عدد مستويات الأولوية

x-max-priority يمكن أن يكون رقمًا كبيرًا، لكن من الناحية العملية:

  • غالبًا 5–10 مستويات كافية جداً.
  • كلما زادت المستويات، زادت تكلفة إدارة وترتيب الرسائل داخليًا.

التوصية: حدّد 3–5 فئات منطقية (Critical, High, Normal, Low) وربطها بأرقام 1–10.

2. تجنب جعل كل الرسائل ذات أولوية عالية

إذا جعلت معظم الرسائل priority = 9 أو 10، فقدت الفائدة من الـ Priority Queue. ضع قواعد واضحة:

  • Critical = فقط للأحداث التي تؤثر مباشرة على أمان المستخدم أو توافر النظام.
  • Normal = معظم الإشعارات اليومية.
  • Low = رسائل تسويقية أو إشعارات يمكن أن تتأخر.

3. استخدم Dead Letter Queue مع نظام الأولوية

في حالة فشل معالجة إشعار (مثلاً فشل إرسال Email أو خطأ في الواجهة مع Firebase)، من الأفضل إرسال الرسالة لـ Dead Letter Queue بدل إعادة المحاولة اللانهائية.

يمكنك الرجوع لمقال كيفية التعامل مع الرسائل الفاشلة في Kafka وRabbitMQ باستخدام Dead Letter Queue لتصميم DLQ متكامل.

4. راقب أداء الـ Queue تحت الضغط

عند وجود ضغط كبير من الرسائل ذات أولوية عالية، قد يحدث:

  • بقاء الرسائل منخفضة الأولوية في الـ Queue لفترات طويلة.
  • زيادة استهلاك الـ Resources لدى RabbitMQ لإدارة الترتيب.

حلول محتملة:

  • تحديد Maximum Queue Length للرسائل منخفضة الأولوية (باستخدام x-max-length).
  • إنشاء Workers إضافية لمعالجة الأولويات المنخفضة عند الحاجة.

5. دمج الـ Priority مع Work Queues وPub/Sub

إذا كان لديك تصميم أعمق لنظام الإشعارات، يمكنك الدمج مع:

مثال معماري كامل لنظام Notification Priority

تصور بسيط لمعمارية متكاملة:

  1. Notification API:
    • واجهات REST/GraphQL تستقبل طلبات إرسال إشعارات.
    • تحدد نوع الإشعار (Security, Marketing, System ...).
    • تحسب priority المناسبة وترسل رسالة إلى RabbitMQ.
  2. RabbitMQ Layer:
    • Exchange من نوع direct أو topic، مثل: notifications.exchange.
    • Queue رئيسية: notifications.priority مع x-max-priority.
    • Optional: DLQ مثل notifications.dlq للأخطاء.
  3. Notification Workers:
    • Consumers يسحبون من notifications.priority.
    • بحسب نوع الإشعار، يتم التوجيه إلى:
      • خدمة Email Sender.
      • خدمة Push Notifications.
      • خدمة WebSocket Real-Time.
  4. Real-Time & Mobile Layer:
    • WebSocket Server لتحديث واجهة المستخدم في الوقت الحقيقي.
    • Firebase / APNs لإرسال Push للتطبيقات.

في هذا التصميم، عندما يرسل النظام إشعارين في نفس اللحظة:

  • إشعار Critical عن محاولة اختراق (priority = 10).
  • إشعار Newsletter أسبوعي (priority = 3).

سيقوم أول Worker متاح بسحب رسالة الاختراق أولًا، ومعالجتها، ثم ينتقل بعدها إلى رسائل الـ Newsletter، حتى لو كانت الـ Newsletter نُشرت قبلها بلحظات.

متى لا يكون Priority Queue هو الخيار الأنسب؟

رغم قوة rabbitmq priority queue notifications، هناك حالات قد لا تكون أفضل اختيار:

  • إذا كان حجم الرسائل ضخم جدًا (ملايين في الدقيقة) ومعظمها High Priority. هنا قد تحتاج لتصميم متعدد الـ Queues مع Scaling كبير.
  • إذا كانت لديك قواعد Routing معقدة حسب نوع المستخدم، الدولة، نوع الجهاز... عندها قد تحتاج لأنظمة توجيه أكثر مرونة مع Topic Exchanges وعدة Queues.
  • إذا كان ترتيب الزمن (FIFO) أهم من الأولوية في بعض السيناريوهات (مثلاً في بعض العمليات المالية المتسلسلة).

الخلاصة

استخدام Priority Queues في RabbitMQ يعطيك طريقة بسيطة وفعّالة للتحكم في أولوية الإشعارات، بحيث:

  • تصل الرسائل العاجلة للمستخدمين أولاً.
  • لا تتسبب الرسائل التسويقية أو العادية في تأخير التنبيهات الحرجة.
  • يمكنك توحيد مسار المعالجة في Queue واحدة مع ترتيب داخلي يعتمد على priority.

بتطبيق ما سبق، يمكنك بناء نظام Notification Priority مرن وقابل للتوسع، يتكامل بشكل طبيعي مع أنظمة RabbitMQ الأخرى لديك، مثل Work Queues، Pub/Sub، وMicroservices، كما وضحنا في: تصميم Notification Service باستخدام RabbitMQ في Microservices.

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

حول المحتوى:

كيف تستخدم Priority Queues في RabbitMQ لإدارة أولوية الإشعارات حسب الأهمية.

هل كان هذا مفيدًا لك؟

أضف تعليقك