الحاجة إلى اكتشاف الخدمات
الحاجة إلى اكتشاف الخدمات
حين تقسّم التطبيق الكبير (monolith) إلى خدمات مصغّرة تكسب القدرة على النشر المستقل والتوسّع المستقل واستقلالية الفرق. لكنك تكسب أيضًا صنفًا جديدًا من المشاكل لم يعانِها التطبيق الكبير قط: كيف تجد الخدمة A الخدمةَ B في وقت التشغيل؟ في التطبيق الكبير الجواب هو استدعاء محلي مباشر. أما في النظام الموزّع فيجب أن يأخذ الجواب بعين الاعتبار إمكانية إعادة تشغيل أي خدمة أو توسيعها أو نقلها إلى مضيف مختلف أو استبدالها في أي لحظة — وغالبًا بدون أي تدخّل بشري.
فخّ العناوين الثابتة المضمّنة في الكود
أكثر الأفكار البديهية هي وضع عنوان URL مباشرة في ملف الإعداد:
ثم في الخدمة المُستدعِية:
هذا النهج يعمل بشكل مثالي في بيئة المختبر. لكنه ينهار فور الانتقال إلى بيئة تشبه الواقع.
لماذا تفشل العناوين الثابتة في السحابة
تُبنى منصات السحابة والحاويات على مبدأ الزوال (ephemerality): النسخ تُنشأ وتُتلف، وعناوين IP تُخصَّص ديناميكيًا، ولا يُعدّ أي مضيف دائمًا. العناوين الثابتة تنتهك جميع هذه الافتراضات.
تخصيص عناوين IP ديناميكيًا
في كل مرة تُعاد فيها تشغيل حاوية Docker أو كبسولة Kubernetes، تُخصّص المنصة عنوان IP جديدًا من مجموعة عناوين. العنوان 192.168.1.42 الذي كان صحيحًا صباح اليوم قد ينتمي بعد الظهر لخدمة مختلفة تمامًا — أو لا توجد خدمة على الإطلاق. العنوان الثابت الذي أضفته في ملف الإعداد يُشير الآن إلى لا شيء، أو ما هو أسوأ، إلى الشيء الخطأ.
التوسّع الأفقي
لنفترض أن حركة المرور ارتفعت وقامت المنصة بالتوسّع التلقائي لخدمة المخزون من نسخة واحدة إلى ثلاث. أصبح لديك الآن ثلاثة عناوين صالحة — :8081 و:8082 و:8083 — لكن ملف إعدادك لا يذكر سوى العنوان الأصلي. الطاقة الإضافية غير مرئية كليًا للخدمات المُستدعِية. تدفع مقابل ثلاث نسخ وتستخدم واحدة فعلًا.
عمليات النشر المتدرّج والتحديث دون توقف
أثناء النشر المتدرّج، تتعايش النسخ القديمة والجديدة مؤقتًا. إذا كان موازن التحميل أو الخدمة المُستدعِية تتتبّع عنوانًا واحدًا فقط، تتراكم الطلبات على نسخة واحدة خلال فترة الانتقال. بقية النسخ — بعضها يشغّل الإصدار القديم وبعضها الجديد — تظل غير مرئية.
الانجراف في الإعداد بين البيئات المختلفة
مع العناوين الثابتة، تحتاج كل بيئة (التطوير، الاختبار، الإنتاج) إلى نسختها الخاصة من كل ملف خصائص. كثيرًا ما ينسى المطوّرون تحديث بيئة واحدة، فيُنشر بناء مرحلة الاختبار لا يزال يستدعي عنوان الإنتاج — أو العكس. وخطأ كهذا عواقبه واسعة.
ما يحلّه اكتشاف الخدمات
يستبدل اكتشاف الخدمات العناوين الثابتة بـسجلّ (registry) — دليل مشترك دائم التحديث لنسخ الخدمات الجارية. بدلًا من السؤال "ما هو العنوان الثابت للمخزون؟"، يسأل المُستدعي "أعطني عنوان أي نسخة سليمة من خدمة المخزون الآن."
العقد الأساسي له وجهان:
- التسجيل (Registration): حين تبدأ خدمة، تُعلن عن عنوانها ومنفذها وعنوان URL للتحقق من صحتها في السجلّ. حين تُغلق بشكل سليم، تلغي تسجيلها. حين تنهار، يطردها السجلّ بعد مهلة قابلة للضبط.
- الاكتشاف (Discovery): حين يحتاج مُستدعٍ للوصول إلى خدمة، يستعلم السجلّ باسم منطقي (مثل
inventory-service) ويحصل على قائمة بعناوين النسخ السليمة. يمكنه حينئذٍ اختيار واحدة — بالتناوب أو عشوائيًا أو حسب زمن الاستجابة — دون معرفة أي شيء عن البنية التحتية.
الاكتشاف من جانب العميل مقابل الاكتشاف من جانب الخادم
ثمة نمطان رئيسيان لاستخدام السجلّ، ومعرفة أيهما ينفّذه إطار عملك أمر مهم:
- الاكتشاف من جانب الخادم: يُرسل المُستدعي الطلب إلى موازن تحميل أو بوابة، تستعلم هي بدورها السجلّ وتُعيد توجيه الطلب. المُستدعي لا يعرف شيئًا عن السجلّ. موازن تحميل تطبيقات AWS وخدمات Kubernetes يعملان بهذه الطريقة.
- الاكتشاف من جانب العميل: المُستدعي نفسه يستعلم السجلّ ويختار نسخة ويُجري استدعاء HTTP مباشرة. Spring Cloud LoadBalancer (بديل Ribbon) ينفّذ هذا النمط. أكثر مرونة لكنه يضع وعيًا بالسجلّ داخل كل خدمة.
Spring Cloud Gateway (الذي يُغطّى لاحقًا في هذا البرنامج التعليمي) يجمع الاثنين في الغالب: تعمل البوابة كنقطة دخول من جانب الخادم للحركة الخارجية لكنها تستخدم الاكتشاف من جانب العميل داخليًا للتوجيه إلى الخدمات الخلفية.
المقايضة في الأنظمة الموزّعة
سجلّ الخدمات هو بحد ذاته مكوّن موزّع، مما يعني أنه يجب أن يكون متاحًا للغاية. إذا تعطّل السجلّ، فإن الخدمات التي تعتمد عليه في قرارات التوجيه لن تستطيع اكتشاف أندادها الجديدة. يعالج Spring Cloud Eureka هذا بـذاكرة تخزين مؤقتة محلية: يُخزّن كل عميل آخر لقطة معروفة من السجلّ ويستمر في التوجيه منها حتى حين يكون خادم السجلّ غير متاح مؤقتًا. هذه مقايضة متعمّدة بين التوافق (consistency) (دائمًا رؤية أحدث قائمة) والتوفّر (availability) (القدرة على التوجيه أصلًا) — تحديدًا الزاوية AP من نظرية CAP.
مقارنة عملية: قبل وبعد
بدون اكتشاف الخدمات — كيف يبدو كود المُستدعي:
مع اكتشاف الخدمات و Spring Cloud LoadBalancer — كيف يبدو نفس الاستدعاء:
كود المُستدعي لا يعرف عنوان IP الحقيقي أبدًا. لا يتغيّر حين تُضاف نسخ أو تُزال أو تُستبدل. السجلّ وطبقة موازنة التحميل تُعالجان كل ذلك بشفافية.
الخلاصة
العناوين الثابتة المضمّنة في الكود حلّ بسيط في ظاهره لكنه ينهار فور أن تصبح بنيتك التحتية ديناميكية — وهو الحال الافتراضي في أي بيئة سحابية أو حاوياتية. المشاكل الجوهرية هي تخصيص IP الديناميكي، والتوسّع الأفقي غير المرئي، وتعقيدات النشر المتدرّج، والانجراف في الإعداد بين البيئات. يحلّ اكتشاف الخدمات هذه المشاكل بإدخال سجلّ يُعيّن الأسماء المنطقية للخدمات إلى عناوين النسخ الحية، وينقل المعرفة بالبنية التحتية خارج كود التطبيق، ويُهيّئ الأرضية لأنماط موازنة التحميل والمرونة التي تبني عليها الدروس التالية. في الدرس القادم ستنفّذ هذا السجلّ باستخدام Spring Cloud Netflix Eureka.