النشر والاشتراك: المواضيع والتوزيع الواسع
النشر والاشتراك: المواضيع والتوزيع الواسع
في الدرس السابق استكشفنا طوابير الرسائل ذات الاتجاه الواحد، حيث يرسل منتج واحد رسالة ويعالجها مستهلك واحد بالضبط. يكسر نمط النشر والاشتراك (Pub/Sub) هذا القيد 1-إلى-1: يمكن لحدث واحد يُنشر أن يُسلَّم إلى عدد كبير من المشتركين المستقلين في آنٍ واحد. هذه القدرة على التوزيع الواسع (Fan-Out) أساسية في معماريات الأحداث المفككة لدى شركات مثل Uber وSpotify وLinkedIn.
الموضوع: وحدة التوجيه
الموضوع هو قناة منطقية مسماة. يكتب الناشرون إلى الموضوع؛ يعلن المشتركون اهتمامهم بموضوع واحد أو أكثر؛ ويتولى الوسيط الاكتشاف والتسليم. فكّر في الموضوع كتردد إذاعي: يبث المذيع على تردد 99.7FM دون أن يعلم من يستمع — السيارات والمطابخ والهواتف تستقبل البث ذاته بصورة مستقلة.
في الأنظمة الكبيرة تُصمَّم قاموس المواضيع بعناية. نمط شائع هو التسمية التراتبية: orders.placed، orders.shipped، orders.cancelled. تدعم بعض الوسطاء (مثل MQTT) أحرف البدل — مشترك في orders.* يتلقى الثلاثة. أما وسطاء آخرون (مثل Kafka) فيستلزمون اسم موضوع محدداً.
التوزيع الواسع: حدث واحد، مستهلكون كثيرون
التوزيع الواسع هو ما يجعل Pub/Sub فعّالاً. تخيّل عميلاً يضع طلباً على منصة تجارة إلكترونية. هذا الحدث الوحيد orders.placed يحتاج إلى:
- تشغيل خدمة المخزون لحجز الكميات
- تشغيل خدمة البريد الإلكتروني لإرسال تأكيد
- تشغيل خط التحليلات لتسجيل عملية التحويل
- تشغيل خدمة كشف الاحتيال لتقييم المعاملة
- تشغيل خدمة الولاء لمنح نقاط
في طابور النقطة إلى النقطة تحتاج خمسة طوابير منفصلة وخمس عمليات إرسال، وسيحتاج النظام لمعرفة كل مستهلك تالٍ. مع Pub/Sub، ينشر النظام مرة واحدة على orders.placed — يتولى الوسيط التوزيع الواسع على المشتركين الخمسة. إضافة مشترك سادس (مثل نظام انتقاء المستودع) لا تستلزم أي تعديل على الجانب الناشر.
الدفع مقابل السحب
يُسلِّم الوسيط الرسائل للمشتركين بأحد أسلوبين:
- الدفع (Push): يستدعي الوسيط المشترك (مثلاً إرسال HTTP Webhook أو الدفع عبر اتصال مفتوح). المشترك سلبي. تعمل Google Pub/Sub وAWS SNS بهذه الطريقة افتراضياً. تسليم سريع، لكن إذا كان المشترك متوقفاً مؤقتاً يجب على الوسيط التخزين المؤقت وإعادة المحاولة.
- السحب (Pull): يستطلع المشترك الوسيط بحثاً عن رسائل جديدة. Kafka وAWS SQS يستخدمان هذا النهج. يتحكم المشترك في معدل استهلاكه الخاص، مما يجعل السحب مرناً بطبيعته أمام المستهلكين البطيئين — يستطلعون ببساطة بوتيرة أبطأ. السحب هو النموذج السائد في الأنظمة عالية الإنتاجية.
مجموعات المستهلكين: التنافس مقابل التوزيع الواسع
دقة تُربك كثيراً من المهندسين: يمكن لـPub/Sub وطوابير النقطة إلى النقطة التعايش في الوسيط ذاته عبر مجموعات المستهلكين (مصطلح Kafka؛ تسميها أنظمة أخرى "subscriptions" أو "queues").
تخيّل خدمة تحليلات تشغّل خمس نسخ عاملة للتوازي. تريد:
- توزيع واسع عبر الخدمات: كلٌّ من التحليلات والبريد والكشف عن الاحتيال يستقبل كل حدث. (هذه اشتراكات / مجموعات مستهلكين منفصلة.)
- تنافس ضمن الخدمة: تتشارك العمال الخمسة لخدمة التحليلات الحمل — كل حدث يعالجه عامل واحد فقط. (هؤلاء العمال يشتركون في مجموعة مستهلكين واحدة.)
يحقق Kafka هذا بأناقة: كل مجموعة مستهلكين تحصل على نسخة مستقلة من بيانات الموضوع؛ داخل المجموعة، تُوزَّع الأقسام (Partitions) بين المستهلكين بحيث تذهب كل رسالة لواحد منهم فقط. عند مليون حدث في الثانية، قد تشغّل 50 قسماً مع 10 نسخ مستهلك لكل مجموعة — كل نسخة تعالج 20,000 حدث/ثانية.
ضمانات التسليم والتوزيع الواسع
تتفاعل ضمانات التسليم مع التوزيع الواسع بطرق مهمة. تعتمد معظم أنظمة Pub/Sub الإنتاجية على التسليم مرة واحدة على الأقل: يعيد الوسيط المحاولة إن لم يتلقَّ إقراراً، مما قد ينتج عنه رسائل مكررة. ولأن الوسيط يوزع لكل مشترك بصورة مستقلة، فإن التكرار الذي يصل لمجموعة التحليلات لا يؤثر على مجموعة البريد — الأعطال معزولة لكل مشترك على حدة. هذه ميزة رئيسية على التوزيع الواسع التزامني ضمن العملية الواحدة، حيث كان مرسل البريد البطيء سيوقف خط التحليلات بالكامل.
تصميم المواضيع: الخشونة مقابل الدقة
من أهم القرارات المبكرة في نظام Pub/Sub هو مدى دقة المواضيع. طرفان:
- موضوع سميك واحد (
events) — كل أنواع الأحداث تسري عبر موضوع واحد. بسيط تشغيلياً؛ لكن المشتركين يجب أن يُرشّحوا بأنفسهم حسب نوع الحدث، مما يهدر المعالجة ويزيد التقارن. - مواضيع دقيقة كثيرة (
orders.placed,orders.shipped, ...) — كل مشترك يستقبل ما اشترك به فقط. نظيف؛ لكن تفشّي المواضيع (آلاف المواضيع) قد يُجهد البيانات الوصفية لدى الوسيط وأدوات المراقبة.
قاعدة عملية جيدة: أنشئ موضوعاً لكل كيان وحدث دورة حياة. لنطاق الطلبات، orders.placed وorders.shipped وorders.cancelled وorders.refunded مناسب. تجنّب إنشاء موضوع منفصل لكل عميل — هذا يقود إلى ملايين المواضيع وهو نمط مضاد في كل وسيط رئيسي.
اختيار نظام Pub/Sub
يُشكّل اختيار الوسيط الكمون والمتانة والترتيب والتكلفة:
- Apache Kafka — أعلى إنتاجية (ملايين رسالة/ثانية)، سجل متين، إمكانية إعادة التشغيل. عبء تشغيلي كبير. الأفضل لخطوط بيانات التدفق وتوريد الأحداث. (يتناوله الدرس الرابع بعمق.)
- Google Cloud Pub/Sub — مُدار بالكامل، عالمي، تسليم مرة واحدة على الأقل، دفع وسحب. خيار جيد افتراضي للخدمات المستضافة على GCP.
- AWS SNS + SQS — SNS هو طبقة التوزيع الواسع؛ SQS هو الطابور المتين لكل مشترك. نمط كلاسيكي: موضوع SNS ← طوابير SQS متعددة، واحد لكل مجموعة مشتركين.
- Redis Pub/Sub — كمون منخفض جداً (أقل من ميلي ثانية)، في الذاكرة فقط. لا استمرارية، لا ضمان تسليم إذا كان المشترك غير متصل. مناسب للإشعارات الفورية المؤقتة؛ غير مناسب لخطوط الأحداث الحرجة.
- NATS / NATS JetStream — خفيف الوزن، مصمم للسحابة، ممتاز للميكروسيرفيسز. يضيف JetStream الاستمرارية وتسليم مرة واحدة على الأقل.
نمط Pub/Sub ركيزة للأنظمة ذات الاقتران الضعيف والقابلة للتوسع الأفقي. إتقان المواضيع والتوزيع الواسع ومجموعات المستهلكين يتيح لك استبدال استدعاءات HTTP الهشة بين الخدمات بعمود فقري رسائل متين وقابل للملاحظة والتوسع المستقل.