حول المحتوى:
شرح كيفية إعادة محاولة إرسال الرسائل والإشعارات عند الفشل باستخدام DLQ وRetry queues.
في أنظمة الإشعارات (Notifications) المعتمدة على RabbitMQ، من الطبيعي حدوث فشل في معالجة بعض الرسائل: خدمة البريد تعطلت، API خارجي لا يستجيب، أو خطأ غير متوقع في الكود. هنا يظهر دور Retry Mechanism لإعادة المحاولة بدون فقدان البيانات أو تكرار الإرسال بشكل عشوائي.
في هذا المقال سنشرح بالتفصيل كيفية تطبيق rabbitmq retry failed messages باستخدام Dead Letter Queue (DLQ) وRetry Queues، مع التركيز على سيناريو إرسال الإشعارات (إيميل، SMS، Push Notification) في أنظمة موزعة.
قبل الدخول في التفاصيل التقنية، من المهم أن نفهم المشكلة التي يحلها Retry Mechanism:
إذا كنت تبني نظام Notifications قابل للتوسع باستخدام Message Queues أو Notification Service باستخدام RabbitMQ في Microservices، فآلية Retry تعد جزءًا أساسيًا من التصميم الصحيح.
في RabbitMQ لا يوجد Retry Mechanism جاهز مبني داخل النظام بالطريقة التي نحتاجها غالبًا، لكن يمكننا بناؤه باستخدام:
الفكرة العامة:
notifications.queue).ack للرسالة.هذا هو الكيو الذي يستقبل رسائل الإشعارات لأول مرة، وليكن اسمه:
notifications.queue
هذا الكيو يتم استهلاكه من Worker/Consumer وظيفته إرسال الإشعار (Email/SMS/Push). في حال الفشل، لا نريد أن نعمل nack + requeue=true باستمرار لأنه سيسبب:
نعرف كيو خاص لإعادة المحاولة، مثال:
notifications.retry.queue
هذا الكيو يحتوي على خصائص مهمة:
عند انتهاء TTL، الرسائل الموجودة في Retry Queue يتم تحويلها تلقائيًا إلى الإكستشينج المحدد، والذي يعيد توجيهها للكيو الرئيسي أو لكيو وسيط جديد.
هذا الكيو مخصص للرسائل التي:
مثال:
notifications.dlq
هذا الكيو عادة لا تتم معالجته تلقائيًا، بل:
شرح مفصل للـ DLQ يمكنك قراءته هنا: كيفية التعامل مع الرسائل الفاشلة في Kafka وRabbitMQ باستخدام Dead Letter Queue.
التحدّي الأساسي في rabbitmq retry failed messages هو: كيف نعرف عدد مرّات المحاولة لكل رسالة؟
الحل الشائع: استخدام Message Headers.
x-retry-count.x-retry-count = 1.المنطق داخل الكود (بشكل عام، بدون لغة محددة):
notifications.queue.ack للرسالة.x-retry-count من الهيدر (إذا لا يوجد، اجعله 0).retryCount < MAX_RETRY: notifications.retry.exchange أو notifications.retry.queue مع x-retry-count = retryCount + 1.notifications.dlq مع تمييزها بأنها failed permanently.ack أو nack بالطريقة المناسبة حتى لا تبقى الرسالة عالقة دون معالجة.هذا النموذج بسيط ومناسب للبدايات:
notifications.retry.30s بمدة TTL = 30 ثانية.x-retry-count للتحكم بعدد المرات.العيب: كل المحاولات يفصل بينها نفس الزمن (30 ثانية مثلًا)، ولا يوجد Backoff متزايد.
لتحسين أداء النظام وتخفيف الضغط، يمكن استخدام أكثر من Retry Queue بزمن متزايد:
notifications.retry.10s (محاولة أولى بعد 10 ثوان).notifications.retry.60s (محاولة ثانية بعد دقيقة).notifications.retry.300s (محاولة ثالثة بعد 5 دقائق). منطق الـ Consumer يقوم بتحديد الكيو المناسب بناءً على x-retry-count:
retryCount = 1 → أرسل إلى retry.10s.retryCount = 2 → أرسل إلى retry.60s.retryCount = 3 → أرسل إلى retry.300s.notifications.dlq.هذا النمط يشبه Exponential Backoff المستخدم بكثرة في Retry Pattern في الأنظمة الموزعة.
لضمان عدم فقدان الرسائل في آلية rabbitmq retry failed messages، يجب الانتباه للنقاط التالية:
durable = true.deliveryMode = 2 (في AMQP) أو ما يعادله، حتى يتم حفظ الرسالة على القرص.بهذا الشكل، حتى لو RabbitMQ Server أعاد التشغيل، لن تفقد رسائلك.
auto-ack=true مع رسائل حساسة مثل الإشعارات.manual ack/nack: ack.ack بعد إعادة النشر إلى Retry أو DLQ (أو nack requeue=false مع وجود DLX مضبوط).هذا يضمن ألا تبقى الرسالة في حالة "غير مؤكدة" تؤدي إلى ازدواجية أو ضياع.
عند تعريف الكيوهات، نستخدم خصائص مثل:
nack بدون إعادة. بهذه الطريقة، عندما يقرر Consumer أنك لا تريد إعادة المحاولة لهذه الرسالة، يمكنك nack مع requeue = false وستنتقل تلقائيًا إلى DLQ.
لنفترض أنك تبني نظام إشعارات يرسل:
تصميم الكيوهات يمكن أن يكون كالتالي:
notifications.email.queuenotifications.sms.queuenotifications.push.queueعند فشل إرسال إيميل بسبب API مزوّد خدمة البريد:
notifications.email.queue.notifications.email.retry.10s مع x-retry-count = 1.retry.60s مع x-retry-count = 2… وهكذا.notifications.email.dlq ليتم مراجعتها يدويًا أو للتواصل مع المستخدم بشكل بديل.هذا يضمن:
آلية rabbitmq retry failed messages ليست مجرد إضافة صغيرة، بل هي جزء أساسي من تصميم أي نظام إشعارات أو نظام موزع يعتمد على RabbitMQ. باستخدام:
يمكنك بناء نظام إرسال إشعارات مرن، موثوق، وقادر على التعامل مع الأعطال المؤقتة بدون فقدان البيانات أو إغراق المستخدم برسائل مكررة.
إذا كنت ترغب في التعمق أكثر في استخدام RabbitMQ في أنظمة الإشعارات، يمكنك الاطلاع على:
بتطبيق هذه المبادئ، ستتمكن من بناء Retry Mechanism قوي في RabbitMQ يحافظ على رسائلك ويضمن تجربة موثوقة للمستخدمين حتى في وجود أعطال مؤقتة في النظام أو الخدمات الخارجية.
شرح كيفية إعادة محاولة إرسال الرسائل والإشعارات عند الفشل باستخدام DLQ وRetry queues.
مساحة اعلانية