مشروع: تصميم طبقة الاتصال
مشروع: تصميم طبقة الاتصال
قضيت الدروس التسعة الماضية في بناء مفردات تقنية: توجيه IP، ونظام أسماء النطاقات، وHTTP/HTTPS، وTCP مقابل UDP، وREST، وgRPC، وWebSockets، وبوابات API، وSSE، والاستطلاع الطويل. يجمع هذا الدرس الختامي كل ذلك معاً في تطبيق عملي. ستعمل على تصميم تطبيق تعاون في الوقت الفعلي لتحرير المستندات (على غرار Notion أو Figma)، وستتخذ كل قرار يتعلق بالبروتوكولات وشكل الواجهة البرمجية من الصفر، مع تبرير صريح لكل اختيار.
النظام الذي نصممه
ملخص المنتج: محرر مستندات تعاوني يمكن لـ 500 مستخدم تحرير نفس مساحة العمل فيه بشكل متزامن. تشمل الميزات: المؤشرات الحية، ومزامنة النصوص الفورية، والتعليقات، ومرفقات الملفات، وتدفق الإشعارات. يجب أن يدعم النظام 10 ملايين مستخدم مسجل مع وصول أقصى للمحررين المتزامنين إلى 50,000.
سنصمم طبقة الاتصال فقط — لا محرك التخزين ولا شبكة توصيل المحتوى — بل البروتوكولات وأشكال الواجهة البرمجية وتدفقات الرسائل التي تربط العملاء بالخوادم والخدمات ببعضها.
الخطوة الأولى — رسم خريطة لكل حدود اتصال
قبل اختيار أي بروتوكول، حدد ما يتواصل فعلياً. في نظامنا، هناك خمسة حدود متمايزة:
- المتصفح ↔ بوابة API — إجراءات المستخدم (إنشاء مستند، دعوة متعاون)
- المتصفح ↔ خدمة التعاون — التعديلات الفورية ومواضع المؤشرات
- المتصفح ↔ خدمة الإشعارات — تحديثات التدفق (تمت إضافة تعليق، ذكر)
- بوابة API ↔ الخدمات المصغرة الداخلية — استدعاءات بين الخدمات (المصادقة، البحث، الفوترة)
- الخدمات ↔ ناقل الرسائل — توزيع الأحداث غير المتزامن (تم حفظ المستند ← تشغيل المفهرس، إشعار المشتركين)
رسم خريطة الحدود أولاً يمنع الخطأ الشائع المتمثل في اختيار بروتوكول واحد لكل شيء ثم محاولة تكييفه مع الحالات التي لا يناسبها.
الخطوة الثانية — تطبيق معايير القرار على كل حدود
الحدود الأولى — المتصفح إلى بوابة API (إجراءات CRUD)
البيانات: إنشاء/قراءة/تحديث/حذف المستندات، وإدارة المستخدمين، ورفع البيانات الوصفية. النمط: طلب-استجابة، مدفوع بإجراء صريح من المستخدم. هدف التأخير: 200–500 ميلي ثانية مقبول. الاختيار: REST عبر HTTPS.
يفوز REST هنا لأن: الموارد تتناسب بشكل طبيعي مع مسارات URL مثل /docs/{id} و/workspaces/{id}/members؛ وتخزين HTTP المؤقت (ETags، Cache-Control) يقلل حمل القراءة؛ ويتعامل معه fetch() في المتصفح بشكل أصلي؛ وتُفهم دلالات الأخطاء (400، 401، 404، 409، 422) من قِبل كل مطور frontend. استخدم هيئة JSON للطلبات والاستجابات. قسّم المجموعات الكبيرة باستخدام التقسيم المستند إلى المؤشر بدلاً من الإزاحة — التقسيم بالإزاحة غير متسق في ظل الكتابات المتزامنة.
/v1/ وليس ترويسة. الترويسات غير مرئية في عناوين URL للمتصفح وأصعب في التوجيه. عندما تكسر عقداً، زِد الإصدار — لا تغيّر الاستجابات بصمت.
الحدود الثانية — المتصفح إلى خدمة التعاون (التعديلات الفورية)
البيانات: تحولات عملياتية أو CRDTs — عمليات دلتا صغيرة جداً مثل "أدخل الحرف X في الموضع 47"، عشرات في الثانية لكل مستخدم نشط. النمط: ثنائي الاتجاه، مستمر، منخفض التأخير، عالي التكرار. الاختيار: WebSockets.
هذه هي حالة الاستخدام المثالية لـ WebSocket. يحتاج كل من العميل والخادم إلى الإرسال في أي لحظة؛ وسيضيف الاستطلاع تأخيراً غير مقبول. اتصال WebSocket واحد لكل علامة تبويب يحمل جميع عمليات المستندات وأحداث المؤشرات وإشارات الحضور بتأخير ذهاب وإياب أقل من 100 ميلي ثانية.
استخدم تنسيق رسائل ثنائي مضغوط مثل MessagePack بدلاً من JSON. عند وجود 50,000 محرر متزامن يولد كل منهم 20 عملية في الثانية، تضيف كثافة JSON تكاليف CPU وعرض نطاق ترددي حقيقية.
الحدود الثالثة — المتصفح إلى خدمة الإشعارات (تحديثات التدفق)
البيانات: أحداث مثل "علّقت آنا على مستندك"، "تصديرك جاهز". النمط: يرسل الخادم بشكل متقطع؛ لا يرسل العميل أبداً. هدف التأخير: 5–30 ثانية مقبول. الاختيار: SSE (الأحداث المرسلة من الخادم).
SSE أبسط من WebSockets للإرسال أحادي الاتجاه: استجابة HTTP/2 عادية لا تنتهي، ترسل قطع text/event-stream. تُعيد واجهة EventSource الأصلية للمتصفح الاتصال تلقائياً عند الانقطاع. تجنب مصافحة ترقية WebSocket، ويتشارك الاتصال نفس اتصال HTTP/2 مع استدعاءات REST.
الحدود الرابعة — بوابة API إلى الخدمات المصغرة الداخلية
البيانات: استدعاءات RPC هيكلية — "تحقق من رمز المصادقة هذا"، "ابحث في المستندات لهذا المستخدم". النمط: داخلي، متزامن، حساس للتأخير، صارم في المخطط. الاختيار: gRPC.
يُسلسل gRPC عبر HTTP/2 باستخدام Protocol Buffers — أصغر بـ 3–10 مرات من JSON وأسرع في الترميز. يوفر عقوداً قوية النوع يُنفذها الكود المولّد. ينخفض التأخير لاستدعاء التحقق من الرمز من ~2 ميلي ثانية (REST/JSON) إلى ~0.3 ميلي ثانية (gRPC/Protobuf) — توفير ذو معنى عندما يقع في المسار الحرج لكل طلب.
الحدود الخامسة — الخدمات إلى ناقل الرسائل (الأحداث غير المتزامنة)
البيانات: أحداث نطاق مثل DocumentSaved، CommentCreated، UserInvited. النمط: توزيع، منتجون ومستهلكون منفصلون، تسليم مرة واحدة على الأقل. الاختيار: رسائل غير متزامنة (Kafka أو ما شابهه) مع مخطط حدث محدد.
هذه الحدود ليست HTTP على الإطلاق. عند حفظ مستند، يجب ألا تُجري خدمة التعاون استدعاءات HTTP متزامنة إلى المفهرس والمُشعر ومسجّل التدقيق — هذا الاقتران الشديد يُبطئ الحفظ ويُتتالى الإخفاقات. بدلاً من ذلك، انشر حدثاً واحداً إلى موضوع؛ يقرأ كل مستهلك داخلي بشكل مستقل.
الخطوة الثالثة — مخطط البنية الكاملة
الخطوة الرابعة — التعامل مع الحالات الصعبة
إعادة الاتصال واستعادة الحالة لـ WebSockets
تنقطع اتصالات الهواتف المحمولة باستمرار. عند إعادة اتصال عميل بخدمة التعاون، يُرسل آخر رقم تسلسلي شاهده (مثلاً { "reconnect": true, "last_seq": 1482 }). يُعيد الخادم إرسال أي عمليات فات العميل منذ ذلك الرقم. بدون هذا، ينحرف العميل المُعاد اتصاله بصمت عن حالة المستند.
التدهور الرشيق لـ SSE
إذا كان المستخدم خلف وكيل HTTP/1.1 يخزّن هيئة الاستجابة مؤقتاً، تنكسر SSE بصمت. نفّذ نبضة قلب كل 30 ثانية. إذا لم يتلقَ العميل نبضة خلال 45 ثانية، انتقل تلقائياً إلى الاستطلاع الطويل كل 15 ثانية. ظهور ارتفاع في حركة الاستطلاع الطويل يُشير إلى مشكلة بنية تحتية.
الضغط الخلفي على ناقل الرسائل
إذا تأخر مستهلك المفهرس (مثلاً أثناء إعادة الفهرسة)، يتراكم تأخر موضوع Kafka. لا تدع المنتجين يتعطلون — بدلاً من ذلك، اضبط حداً أقصى للتنبيه (مثلاً 100,000 رسالة) وقُم بتوسيع المستهلكين أفقياً. يجب ألا تنتظر خدمة التعاون أبداً على المستهلكين البعيدين.
الخطوة الخامسة — جدول ملخص القرارات
العقلية التي تأخذها معك
المخرج الحقيقي من هذا التمرين ليس اختياراً محدداً للبروتوكول — بل هو إطار اتخاذ القرار:
- الاتجاه: أحادي (SSE) أم ثنائي الاتجاه (WebSocket)؟
- التكرار: متقطع (REST) أم مستمر (WebSocket / gRPC streaming)؟
- الاقتران: متزامن (REST، gRPC) أم غير متزامن (ناقل الرسائل)؟
- الجمهور: عملاء خارجيون يحتاجون إلى قابلية التشغيل البيني (REST) أم خدمات داخلية تحتاج إلى الأداء (gRPC)؟
- نمط الفشل: ماذا يحدث إذا كان المستهلك بطيئاً أو معطلاً؟ هل يتعطل المنتج؟
الشبكات والاتصالات هي النسيج الضام لكل نظام موزع. لديك الآن المفردات الكاملة وإطار القرار. طبّقهما باستمرار في كل مرة ترسم فيها مخططاً معمارياً.