في هذه المقالة سوف نتحدث عن بوابة وواجهة العبور الى ال Backends، ففي التطبيقات الضخمة التقليدية Monolithic يكون هناك رابط واحد Endpoint يمثل بوابة الدخول الى ذلك النظام. بينما في ال Microservices فكل خدمة مصغرة لديها ال Endpoints الخاصة بها، وسوف نتحدث تأثير هذا الأمر على التطبيقات Clients وكيف يفيد استخدام ال API Gateway بها.
محتويات السلسلة:
- مقدمة حول ال Microservices
- استخدام ال API Gateway في بناء Microservices (المقالة الحالية)
- التواصل Communications بين الخدمات المصغرة
مقدمة عن ال API Geteway
لنفرض أنك تود تطوير تطبيق موبايل Native Mobile عبارة عن سلة تسوق Shopping Application، بكل تأكيد سوف تحتاج لعمل صفحة للمنتجات، وصفحة لتفاصيل المنتج والتي تعرض كل التفاصيل الخاصة بذلك المنتج. الصورة التالية توضح الخطوات بدءاً من صفحة تفاصيل المنتج الى وضعه في السلة في تطبيق أمازون.
في الصورة الأولى (من اليسار) سوف تجد أن صفحة التفاصيل بها العديد من المعلومات وهي:
- المعلومات الأساسية حول المنتج مثل الاسم، الوصف، السعر
- عدد الأصناف الحالية في سلة التسوق
- تاريخ الطلبات Order History
- تعليقات العملاء Customer Reviews
- معلومة من المستودع بأنه قارب على النفاذ Inventory Warning
- خصائص الشحن Shipping
- مقترحات Recommendations عدة لشراء منتجات شائعة، منتجات اشتروها العملاء بعد شرائهم لهذا المنتج، منتجات شاهدوها العملاء بعد شراءهم لهذا المنتج
في التطبيقات التقليدية Monolithic سوف يقوم التطبيق بجلب بيانات المنتج من خلال REST API مشابه لما يلي:
GET api.company.com/productdetails/productId
وبعدها يقوم ال backend بعمل عدة استعلامات من قاعدة البيانات وارجاع النتيجة للعميل، وفي حالة كان هناك Load Balancer فسوف يقوم بتوجيه الطلب لأي Instance لكي تخدم هذا الطلب وترجع النتيجة المطلوبة.
لكن في حالة الخدمات المصغرة Microservices فهذه البيانات التي تُعرض في صفحة التفاصيل لا تتواجد في خدمة واحدة، وانما متواجدة في عدة خدمات، على سبيل المثال:
- خدمة السلة Shopping Cart Service: وهي تحتوي على العناصر في السلة
- خدمة الطلبات Order Service: وهي تحتوي على تواريخ الطلبات
- خدمة المنتجات Catalog Service: وهي تحتوي على معلومات المنتجات الأساسية
- خدمة التقييمات Review Service: وهي تحتوي على اراء المستخدمين
- خدمة المستودع Inventory Service: وهي التي تظهر معلومات او تحذيرات بخصوص الأصناف
- خدمة الشحن Shipping Service: وهي التي تعرض تفاصيل الشحن، التكاليف، وتتواصل مع مقدمي خدمات الشحن Shipping Providers من خلال ال APIs فيما بينهم.
- خدمة المقترحات Recommendation Service: وهي التي تقترح الأصناف والمنتجات
الصورة التالية توضح هذه الخدمات، وسوف يكون السؤال كيف يمكن أن يتواصل التطبيق Mobile Client مع هذه الخدمات:
التواصل المباشر بين التطبيق مع الخدمات المصغرة Direct Communications
نظرياً يمكن للتطبيق بأن يقوم باستدعاء كل هذه الخدمات المصغرة بشكل مباشر، فسوف يكون لكل خدمة مصغرة Public Endpoint لها مثلاً https://service.api.company.name وداخلياً قد يكون هناك load Balancer لها يقوم بتوجيه الطلب الى أحد ال Instances التي تعمل من هذه ال Microservices.
لسوء الحظ هناك عدة مشاكل في هذا النوع:
- أولاً: التطبيق سوف يحتاج لمعرفة كل ال Endpoints لكل الخدمات المصغرة والتي قد تتجاوز المئات، وهذا يجعله مليء بالروابط، والمشكلة انه سوف يتواصل معها بشكل مباشر (لفتح صفحة تفاصيل المنتجات فيحتاج عمل 7 استدعاءات منفصلة) وهذا يعني استهلاك كبير للبيانات او الانترنت، ولذلك هذا الأمر غير عملي.
- ثانياً: المشكلة الأخرى ان بعض الخدمات المصغرة قد تستخدم بروتوكولات ليس مناسبة مع ال Web، مثلاً أحد الخدمات قد تستخدم RPC وأخرى تستخدم AMQP لل Messaging، وهذه البروتوكولات ليست مناسبة للمتصفحات Not Web Friendly، ولا حتى عند مرورها على الجدر النارية Firewalls، وانما هي مناسبة للتواصل الداخلي Internally.
- ثالثاً: وجود هذه الروابط في التطبيق قد يجعل عملية تحسين Refactoring الخدمات المصغرة أمراً صعباً، فقد يحتاج المطورين الى دمج خدمتين أو تقسيم خدمة الى أكثر من خدمة مع مرور الوقت، وبالتالي يجب أن يتم تحديث التطبيق بهذا الأمر، وهذا ما يعد عملية صعبة أيضاً.
- رابعاً: التواصل المباشر بين التطبيق والخدمات المصغرة سوف يفرض وجود آليات الحماية Security والتحقق على مستوى جميع هذه الخدمات، وهذا ما يضيف عبئ إضافي لها.
وبالتالي نستطيع القول بأن هذا الأسلوب (التواصل المباشر) لا يستخدم ويندر من يقوم باستخدامه، سوف نشاهد الحل الأنسب فيما يلي.
استخدام ال API Gateway
ال API Gateway هو سيرفر أو خدمة (تقدم RESTful API) تمثل بوابة العبور الى النظام ككل، وهو يشبه Façade Design Pattern، حيث يخفي Encapsulate كل تفاصيل النظام الداخلية ويقدم API مخصصة Tailored لكل Client، وقد يكون لديه مسؤوليات أخرى مثل التحقق Authentication، المراقبة Monitoring، توزيع الضغط Load Balancing، الحفظ المؤقت Caching، التحكم في الطلبات القادمة Traffic Control وغيرها.
الصورة التالية توضح كيف أن استخدام API Gateway في المعمارية للمشروع:
ال API Gateway سوف يكون مسؤولاً عن توجيه الطلبات، التحويل بين البروتوكولات Protocol Translations وغيرها، فكل الطلبات سوف تأتي عبر هذه البوابة ويقوم بتوجيهها الى الخدمة المناسبة. وقد يقوم ال API Getaway بالاتصال بعدة خدمات وجلب النتيجة وارجاعها في ال Response للتطبيق.
أيضاً قد يزود ال API Gateway أي Client ب API له، مثلاً سوف يقوم بعمل Coarse-grained API لل Mobile Apps. فيقوم التطبيق باستدعاء الرابط /productdetails?productId=xxx ومن ثم يقوم ال API Gateway داخلياً بجلب البيانات من كل الخدمات المصغرة الأخرى وارجاع النتيجة، وهكذا حصل التطبيق على النتيجة في طلب واحد Single Request.
أحد الأمثلة على أهمية تخصيص ال API بحسب الاستعمالات هو Netflix API Gateway حيث كانت في البدء هي API لعمل streaming service لكل أنواع الأجهزة (جوالات، تلفزيونات، العاب الخ) ولكن لم تكن مناسبة one-size-fit-all وقرروا بناء tailored API لكل نوع من الأجهزة، وال API Gateway يستقبل Billions الطلبات في اليوم الواحد.
فوائد وعيوب ال API Gateway
أحد أهم الفوائد في ال API Gateway هي أنها تخفي كل التفاصيل الداخلية وتقدم API بسيطة للتطبيقات، وهذا يقلل من عدد الطلبات Round Trips بين النظام والتطبيق ويسهل أيضاً أكواد التطبيقات Client Apps.
من الفوائد الأخرى:
- ال Authentication: حيث يقوم ال API Gateway بكل التحقق من ال Tokens وبعدها يرسل الطلبات الى الخدمات المصغرة الداخلية Downstream Services التي لن تحتاج الى اجراء أي تحقق من هذه ال Tokens.
- الحماية Security: هذه البوابة عادة تتعامل مع الاتصال الآمن Transport Security بمعنى انه ال Clients سوف يتصلوا بها HTTPS وهي بعد أن يصل الطلب الآمن تقوم بعمل SSL Termination وترسل الطلب الداخلي للخدمات الداخلية بدون SSL.
- التحكم في ال Traffic: فيمكن عمل IP Filtering والسماح أو إيقاف الوصول لمجموعة من ال IPs.
- تحويل شكل البيانات Transformation: مثلاً قد ترجع الخدمة الداخلية النتيجة ب XML format ويقوم ال API Gateway بتحويلها الى JSON Format.
- ال Caching: فمثلاً إذا كانت هناك بيانات لا تتغير أو تأخذ وقتاً لحسابها، فيمكن وضعها على الكاش ويقوم ال API Gateway باستخدامها من الذاكرة المؤقتة.
لكن هناك بعض المشاكل في ال API Gateway، فلأنها نقطة العبور لكل الطلبات فيجب أن تعمل بشكل دائم Highly Available ويجب أن يتم تطويرها وإدارة والعمل عليها أيضاً كما في بقية الخدمات المصغرة. ومن المخاطر أن تكون هذه البوابة عائق للمطورين، لأنهم يحتاجوا لتحديثها لكي تستطيع معرفة ال Endpoints الجديدة لكل خدمة مصغرة، عموماً هذه ليست مشاكل مقلقة، فكل الاستخدامات السليمة Real-World Applications تستخدم ال API Gateway. وهناك حلول أصبحت تستكشف الروابط تلقائياً بدون أي تدخل يدوي.
تطبيق ال API Gateway
حتى هذه اللحظة تحدثنا عن الدوافع Motivations وراء ال API Gateway وأيضاً النقاط الإيجابية والسلبية Trade-Offs بها، فسوف نتحدث عن بعض المشاكل التصميمية التي تحتاج للنظر لها عن تطبيقك لل API Gateway.
الأداء Performance والتوسعة Scalability
لأن ال API Gateway هي بوابة العبور للنظام فيجب أن يكون أدائها عالياً وتستحمل الضغط، وهذا يستوجب دعم مفاهيم ال asynchronous وال nonblocking I/O ويمكن بنائها من خلال التقنيات التي تدعمها اللغات المختلفة.
استخدام أسلوب ال Reactive Programming Model
يقوم ال API Gateway بمعالجة الطلبات عن طريق توجيهها الى أحد الخدمات المصغرة، أو استدعاء عدة خدمات مصغرة وتجميع النتيجة Aggregate Result وارجاعها في ال Response. وفي حالة استدعاء عدة خدمات مصغرة، فقد لا تكون هناك اعتمادية بينهم، وهنا قد يكون من المستحسن لتقليل الوقت استدعاء هذه الخدمات في آن واحد Concurrently، لكن بعض الأحيان قد تكون هناك اعتماديات وخطوات معينة، مثلاً في البدء يتم التحقق من الطلب من خلال ال Authentication Service، وبعدها جلب بيانات العميل من الخدمة المخصصة لذلك، وبعدها مثلاً ارجاع تفاصيل المنتج وهكذا.
كتابة هذه ال API Composition باستخدام ال asynchronous callback سوف يؤدي لتعقيد ال Callback hell، ويصبح الكود معقداً وصعباً للفهم وعرضة للأخطاء والمشاكل. ولذلك عادة ما يتم كتابة ال API Gateway بطريقة Declarative Style من خلال Reactive Model. وأغلب اللغات تدعم هذا الأسلوب مثلاً Future في سكالا، CompltableFuture في جافا 8، promise في جافا سكربت، كما أن هناك عدة مكتبات Rxjava وغيرهم. هذا الأسلوب يسهل كتابة أكواد وتكون Efficient في ال API Gateway.
التواصل بين الخدمات Service Invocation
الخدمات المصغرة هي عبارة عن أنظمة موزعة Distributed System وللتواصل فيما بينهم يجب ان تستخدم آلية مناسبة للتواصل Inter-Process Communication، وهناك أسلوبين للتواصل، الأول Asynchronous باستخدام ال Messaging وهناك الكثير من الخدمات أو المكتبات لها، النوع الثاني وهو Synchronous باستخدام ال HTTP. وعادة ما تستخدم الأنظمة النوعين بحسب أنواع الخدمات المصغرة فيها.
اكتشاف الخدمات Service Discovery
ال API Gateway يحتاج لمعرفة ال Endpoint لكل من الخدمات المصغرة لكي يتواصل معها (عنوان ال IP ورقم المنفذ Port)، ومن الممكن أن يتم كتابتها يدوياً لكنها عملية صعبة وقد تكون مستحيلة لأن بعض الخدمات تتغير أماكنها بشكل تلقائي مثلاً بسبب ال Auto scaling او ال Upgrades، وبعض الخدمات قد تكون ثابتة في موقع معين مثلاً Messaging، لكن ال Application Microservices غالباً متغيرة، ولذلك يجب استخدام آلية لاكتشاف هذه الخدمات بشكل تلقائي، وهناك طريقة Server-Side Discovery أو Client-Side Discovery، وفي مقالات تالية سوف نوضح هذه الجزئيات بتفاصيل أكبر.
التعامل مع الفشل الجزئي Handling Partial Failures
من التحديات التي تحتاج لحلول عند تطبيق ال API Gateway هي التعامل مع الفشل الجزئي لأحد الخدمات، وهذه المشكلة تحدث في جميع الأنظمة الموزعة عندما تنادي خدمة مصغرة خدمة أخرى ولكن الثانية لم تكن متواجدة أو تأخرت في الرد. وهنا يجب على ال API Gateway ألا ينتظر Blocking الى الأبد لأي من هذه الخدمات، ويتعامل مع المشكلة بما يتناسب مع طبيعة الخدمة. مثلاً إذا كانت الخدمة هي Recommendation Service وتم استدعائها اثناء مرحلة جلب تفاصيل المنتج ولم تستجيب، فال API Gateway يجب أن يرجع تفاصيل المنتج لأنه ما زال مفيداً للمستخدم. وجزئية ال Recommendations يمكن أن تكون فارغة او تبدل بقائمة افتراضية بأكثر 10 خدمات طلباً. لكن بعض الخدمات قد يضطر ال API Gateway ارجاع المشكلة لل Client مثلاً إذا كانت ال Product Information Service لا تستجيب فهنا قد يرجع الخطأ للعميل بتلك المشكلة.
ومن الممكن أن تقوم ال API Gateway بإرجاع نتائج محسوبة مسبقاً Cached Data إذا كانت متوفرة، مثلاً إذا كانت تفاصيل المنتج متواجدة في ال Cache (سواءً بداخل ال API Gateway أو من خدمة منفصلة لها) فيمكن أن يرجعها، وبالتالي يضمن النظام أن الفشل الداخلي لأحد الخدمات لا يؤثر على تجربة المستخدم.
وكثير من المكاتب المخصصة لاستدعاء الخدمات Service Invocation قد تقوم بعمل Timeout إذا تجاوز الطلب مدة معنية Threshold، وتطبق مفهوم ال Circuit Breaker وبالتالي لن يحتاج ال Client الى الانتظار طويلاً في حال لم تستجيب الخدمة إذا تجاوزت المدة المحددة، وقد تسمح ايضاً بتعريف Fallback Action تقوم من خلالها بالقراءة من ال Cache او ارجاع أي بيانات افتراضية حينها.
خلاصة
لأغلب الأنظمة التي تستخدم مفهوم ال Microservices فاستخدام ال API Gateway كبوابة عبور للنظام يعد أمراً مهماً، حيث تستقبل هذه البوابة كل الطلبات وتقوم بعمل ما يلزم تجاهها، وتوفر Custom API لكل نوع مختلف من ال Clients وهذا ما يجعله أكثر مناسبة لذلك ال Client. ايضاً تستطيع إخفاء الفشل Mask Failures في أحد الخدمات المصغرة وترجع نتيجة محسوبة مسبقاً Cached Data أو أي بيانات افتراضية إذا أمكن.
المقالة التالية سوف نتحدث عن طرق التواصل بين هذه الخدمات المصغرة.
المصادر
مقالة Building Microservices:Using an API Gateway في موقع nginx
Great article.
Thanks
مقال اكثر من رائع شكرا لك
ملخص مفيد جدا شكرا