أنظمة بحجم المنتجات

حدثٌ واحد، ملايين التسليمات

مستخدم يكتب تعليقاً واحداً. مليون متابع يجب أن يعرفوا — عبر push وSMS وemail — خلال ثوانٍ، دون أن ينتظر صاحب التعليق، ودون أن تشتعل الخوادم. هذا هو نظام الإشعارات، وهو المكان الذي تتوقف فيه الطوابير وتحديد المعدل وإعادة المحاولة عن كونها نظرية.

المسار
  1. يحدث حدث — تعليق، طلب شراء، انخفاض سعر. الخدمة المُصدِرة تنشره وتمضي؛ لا تنتظر أبداً أن يرد مليون هاتف.
  2. خدمة الإشعارات تُغني الحدث: من يجب إخباره، وعلى أي قناة، وبأي نص — بعد فحص إعداداته. ثم توزّع على طابور رسائل (message queue) لكل قناة: push وSMS وemail.
  3. العمال (workers) يسحبون من كل طابور وينادون مزودي الخدمة الخارجيين: APNs وFCM للـpush، وبوابة SMS للرسائل النصية، وSMTP للبريد.
  4. الإخفاقات تُعاد محاولتها بتباعد تصاعدي (backoff)؛ والرسائل التي تستمر في الفشل تهبط في طابور الرسائل الميتة (dead-letter queue) حيث يفحصها إنسان أو سكربت بدل أن تضيع بصمت.
المبدأ

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

مجموعة عمال لكل قناة، وحجمها يفرضه المزوّد

‏push وSMS وemail تحصل على طوابير منفصلة ومجموعات عمال منفصلة، لأن أشكالها تختلف جذرياً: الـpush إلى APNs يكلف كسوراً من السنت ويتحرك بسرعة، ورسالة SMS تكلف مالاً حقيقياً لكل رسالة وقد لا تقبل البوابة سوى 100 رسالة في الثانية لكل مُرسِل، والبريد يزحف عبر SMTP. حدّد حجم كل مجموعة بحجم قناتها — وبسقف مزوّدها أيضاً. فـAPNs وFCM وكل بوابة SMS تحدّد معدلك، وتجاوز الحد لا يعني فشل رسائل فحسب؛ بل قد يؤدي إلى خنق حسابك أو حظره كلياً. لذا ضع token bucket لكل مزوّد أمام كل مجموعة، ودع حد المزوّد — لا طموحك — هو الذي يضبط معدل التصريف.

أعد المحاولة بصدق، وازِد التكرار دائماً

الإرسال الفاشل يُعاد بخفض تدريجي تصاعدي (exponential backoff) — ثانية، ثانيتان، أربع، ثمانٍ — مع عشوائية (jitter)، حتى لا يصطدم عطل عابر لدى المزوّد بقطيع متزامن من إعادات المحاولة. وبعد عدد ثابت من المحاولات تنتقل الرسالة إلى طابور الرسائل الميتة (dead-letter queue)، حيث تنتظر الفحص بدل أن تعطّل الطابور أو تختفي. لكن إعادة المحاولة تصطدم بحقيقة: الطوابير تسلّم «مرة واحدة على الأقل»، فيمكن أن تصل المهمة نفسها مرتين حتى دون أي فشل. الحل هو مفتاح idempotency — معرّف الحدث مع المستلِم والقناة — يُسجَّل قبل الإرسال؛ والعامل الذي يرى مفتاحاً أرسله سابقاً يسقط النسخة المكررة ببساطة. بدونه، كل إعادة محاولة تخاطر بإرسال رسالتين نصيتين للمستخدم عن طلب واحد.

ليست كل الرسائل متساوية

رمز التحقق لمرة واحدة (OTP) يجب أن يصل في ثوانٍ؛ أما الحملة التسويقية فيمكنها الانتظار دقائق. لذا وجّه بحسب الأولوية: امنح رموز OTP وتنبيهات الأمان طابورها عالي الأولوية الذي يُصرَّف أولاً، ودع مهام الترويج تملأ السعة المتبقية. ووفّر على نفسك العمل حيثما أمكن: افحص طلبات إلغاء الاشتراك وساعات الهدوء وإعدادات القنوات قبل أن تُدرَج المهمة في الطابور أصلاً، لا عند العامل. فكل إشعار يُرشَّح مبكراً هو سعة لم تنفقها ومستخدم لم تزعجه — ورسالة طلب صاحبها ألا تصله أسوأ من غيابها.

المُحفِّزحدث في النظامخدمة الإشعاراتيفحص التفضيلات وopt-outطابور Pushعمال PushAPNs / FCMثم أجهزة المستخدمطابور SMSعمال SMSبوابة SMSثم أجهزة المستخدمطابور Emailعمال EmailSMTPثم أجهزة المستخدمإعادة محاولة → DLQفشل
الحدث ← الإغناء وفحص الإعدادات ← طوابير لكل قناة (مسار أولوية لرموز OTP) ← مجموعات عمال مخنوقة بحسب كل مزوّد ← APNs / FCM / بوابة SMS / SMTP، مع إعادات محاولة تنتهي إلى طابور الرسائل الميتة.
تحقّق سريع

بوابة SMS تسمح بـ100 رسالة في الثانية، وتخفيض خاطف يضع مليوني رسالة في الطابور، والطابور يسلّم كل مهمة «مرة واحدة على الأقل». امشِ في التصميم: ما الذي يشكّل الإنتاجية وإعادات المحاولة والتكرارات؟ (token bucket يخنق مجموعة عمال SMS عند 100/ثانية، فيُصرَّف الدفعة على مدى ‏~5.5 ساعة — أخبر الإدارة أو اشترِ سعة إرسال أكبر. الإخفاقات تُعاد بخفض تدريجي تصاعدي مع jitter وبسقف ثابت من المحاولات، ثم تهبط في طابور الرسائل الميتة. ولأن التسليم «مرة واحدة على الأقل» مع إعادات المحاولة قد يكرر الإرسال، تحمل كل مهمة مفتاح idempotency — الحدث + المستلِم + القناة — يُفحص قبل مناداة البوابة، فلا يستلم أي مستخدم الرسالة نفسها مرتين.)

الخلاصة

نظام الإشعارات مشكلة تخزين مؤقت متنكرة في زي التسليم: طوابير لكل قناة تمتص الارتفاع، ومجموعات العمال تفرّغها بالضبط بالمعدل الذي يتحمّله كل مزوّد، والـbackoff وطوابير الرسائل الميتة تجعل الفشل قابلاً للنجاة، ومفاتيح الـidempotency تجعل إعادات المحاولة آمنة، ومسارات الأولوية تضمن ألا ينتظر رمز OTP خلف النشرة البريدية.

📌 افعل هذا الاثنين

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

أنظمة بحجم المنتجات