تصميم الاتصال بين الخدمات للخدمات الصغيرة

Azure DevOps

يجب أن يكون الاتصال بين الخدمات الصغيرة فعالاً وقوياً. مع تفاعل الكثير من الخدمات الصغيرة لإكمال نشاط تجاري واحد، يمكن أن يكون هذا تحدياً. في هذه المقالة، نلقي نظرة على المفاضلات بين الرسائل غير المتزامنة مقابل واجهات برمجة التطبيقات المتزامنة. ثم نلقي نظرة على بعض التحديات في تصميم اتصالات مرنة بين الخدمات.

التحديات

فيما يلي بعض التحديات الرئيسية الناشئة عن الاتصال من خدمة إلى خدمة. تم تصميم شبكات الخدمة، الموضحة لاحقاً في هذه المقالة، للتعامل مع العديد من هذه التحديات.

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

  • Retry. قد تفشل مكالمة الشبكة بسبب خطأ عابر يختفي من تلقاء نفسه. بدلاً من الفشل التام، يجب على المتصل عادةً إعادة العملية لعدد معين من المرات، أو حتى انقضاء فترة المهلة المكونة. ومع ذلك، إذا لم تكن العملية خاملة، فقد تؤدي إعادة المحاولة إلى آثار جانبية غير مقصودة. قد تنجح المكالمة الأصلية، لكن المتصل لا يتلقى أي رد. إذا أعاد المتصل المحاولة، فقد يتم استدعاء العملية مرتين. بشكل عام، ليس من الآمن إعادة محاولة طرق POST أو PATCH، لأنه ليس هناك ما يضمن أنها ستكون ذات جدوى.

  • Circuit Breaker. يمكن أن يتسبب عدد كبير جداً من الطلبات الفاشلة في حدوث اختناق، حيث تتراكم الطلبات المعلقة في قائمة الانتظار. قد تحتوي هذه الطلبات المحظورة على موارد نظام مهمة مثل الذاكرة، والخيوط، واتصالات قاعدة البيانات، وما إلى ذلك، ما قد يتسبب في حدوث حالات فشل متتالية. يمكن لنمط قاطع الدائرة أن يمنع الخدمة من المحاولة المتكررة لعملية من المحتمل أن تفشل.

موازنة التحميل. عندما تستدعي الخدمة "A" خدمة "B" يجب أن يصل الطلب إلى مثيل جاري من الخدمة "B". في Kubernetes، يوفر نوع المورد Service عنوان IP ثابتاً لمجموعة من البودات pod. يتم إعادة توجيه حركة مرور الشبكة إلى عنوان IP الخاص بالخدمة إلى حجرة عن طريق قواعد iptable. بشكل افتراضي، يتم اختيار pod عشوائي. يمكن أن توفر شبكة الخدمة (انظر أدناه) خوارزميات أكثر ذكاءً لموازنة الحمل استناداً إلى زمن الانتقال الملحوظ أو القياسات الأخرى.

التتبع الموزع. قد تمتد عملية واحدة عبر خدمات متعددة. يمكن أن يجعل ذلك من الصعب مراقبة الأداء العام للنظام وصحته. حتى إذا قامت كل خدمة بإنشاء سجلات وقياسات، بدون طريقة لربطها ببعضها، فإن استخدامها محدود. تتناول مقالة التسجيل والمراقبة المزيد عن التتبع الموزَّع، لكننا نذكرها هنا على أنها تحدٍ.

تعيين إصدار الخدمة. عندما يتوزع فريق إصداراً جديداً من الخدمة، يجب عليهم تجنب كسر أي خدمات أخرى أو عملاء خارجيين يعتمدون عليها. بالإضافة إلى ذلك، قد ترغب في تشغيل تعيين إصدارات متعددة من الخدمة جنباً إلى جنب، وتوجيه الطلبات إلى إصدار معين. راجع تعيين إصدار واجهة برمجة التطبيقات لمزيد من المناقشة حول هذه المشكلة.

تشفير TLS ومصادقة TLS المتبادلة. لأسباب أمنية، قد ترغب في تشفير نسبة استخدام الشبكة بين الخدمات باستخدام TLS، واستخدام مصادقة TLS المتبادلة لمصادقة المتصلين.

الرسائل المتزامنة مقابل الرسائل غير المتزامنة

يوجد نوعان من أنماط المراسلة الأساسية التي يمكن للخدمات الصغيرة استخدامها للتواصل مع الخدمات الصغيرة الأخرى.

  1. اتصال متزامن. في هذا النمط، تستدعي الخدمة واجهة برمجة تطبيقات تعرضها خدمة أخرى، باستخدام بروتوكول مثل HTTP أو gRPC. هذا الخيار هو نمط رسائل متزامن لأن المتصل ينتظر استجابة من جهاز الاستقبال.

  2. تمرير رسالة غير متزامن. في هذا النمط، ترسل الخدمة رسالة دون انتظار استجابة، وتقوم خدمة واحدة أو أكثر بمعالجة الرسالة بشكل غير متزامن.

من المهم التمييز بين الإدخال / الإخراج غير المتزامن والبروتوكول غير المتزامن. يعني الإدخال / الإخراج غير المتزامن عدم حظر مؤشر ترابط الاستدعاء أثناء اكتمال الإدخال / الإخراج. هذا مهم للأداء، لكنه تفاصيل تنفيذ من حيث البنية. يعني البروتوكول غير المتزامن أن المرسل لا ينتظر الرد. HTTP هو بروتوكول متزامن، على الرغم من أن عميل HTTP قد يستخدم الإدخال / الإخراج غير المتزامن عندما يرسل طلباً.

هناك مفاضلات لكل نمط. يعد الطلب / الاستجابة نموذجاً مفهوماً جيداً، لذا قد يبدو تصميم واجهة برمجة التطبيقات أكثر طبيعية من تصميم نظام المراسلة. ومع ذلك، تتميز المراسلة غير المتزامنة ببعض المزايا التي يمكن أن تكون مفيدة في بنية الخدمات الصغيرة:

  • اقتران مخفض. لا يحتاج مرسل الرسالة إلى معرفة المستهلك.

  • تعدد المشتركين. باستخدام نموذج عام / فرعي، يمكن لعدة مستهلكين الاشتراك لاستقبال الأحداث. راجع نمط البنية المستند إلى الأحداث.

  • عزل الفشل. إذا فشل المستهلك، فلا يزال بإمكان المرسل إرسال الرسائل. سيتم التقاط الرسائل عندما يتعافى المستهلك. هذه القدرة مفيدة بشكل خاص في بنية الخدمات الصغيرة، لأن لكل خدمة دورة حياة خاصة بها. قد تصبح الخدمة غير متاحة أو يتم استبدالها بإصدار أحدث في أي وقت. يمكن للرسائل غير المتزامنة التعامل مع فترات التوقف المتقطعة. من ناحية أخرى، تتطلب واجهات برمجة التطبيقات المتزامنة إتاحة الخدمة المتلقية للمعلومات وإلا فشلت العملية.

  • الاستجابة. يمكن لخدمة المنبع الرد بشكل أسرع إذا لم تنتظر خدمات المصب. وهذا مفيد بشكل خاص في بنية الخدمات الصغيرة. إذا كانت هناك سلسلة من تبعيات الخدمة (الخدمة A تستدعي B، والتي تستدعي C، وما إلى ذلك)، فإن انتظار المكالمات المتزامنة يمكن أن يضيف كميات غير مقبولة من زمن الانتقال.

  • تسوية التحميل. يمكن أن تعمل قائمة الانتظار كمخزن مؤقت لتسوية حمل العمل، بحيث يمكن للمستقبلات معالجة الرسائل بمعدلاتها الخاصة.

  • سير العمل. يمكن استخدام قوائم الانتظار لإدارة سير العمل، عن طريق تأشير الرسالة بعد كل خطوة في سير العمل.

ومع ذلك، هناك أيضاً بعض التحديات لاستخدام الرسائل غير المتزامنة بشكل فعال.

  • الاقتران بالبنية الأساسية للمراسلة. قد يؤدي استخدام بنية أساسية معينة للمراسلة إلى اقتران وثيق بهذه البنية الأساسية. سيكون من الصعب التبديل إلى بنية أساسية أخرى للرسائل لاحقاً.

  • زمن الانتقال. قد يصبح زمن الانتقال من طرف إلى طرف لعملية ما مرتفعاً إذا امتلأت قوائم انتظار الرسائل.

  • التكلفة. في حالة معدلات النقل العالية، يمكن أن تكون التكلفة النقدية للبنية الأساسية للرسائل كبيرة.

  • التعقيد. معالجة الرسائل غير المتزامنة ليست مهمة تافهة. على سبيل المثال، يجب أن تتعامل مع الرسائل المكررة، إما عن طريق إلغاء التكرار أو عن طريق جعل العمليات خاملة. من الصعب أيضاً تنفيذ دلالات الطلب والاستجابة باستخدام الرسائل غير المتزامنة. لإرسال رد، أنت بحاجة إلى قائمة انتظار أخرى، بالإضافة إلى طريقة لربط رسائل الطلب والرد.

  • معدل النقل إذا كانت الرسائل تتطلب دلالات قائمة الانتظار، يمكن أن تصبح قائمة الانتظار عنق الزجاجة في النظام. تتطلب كل رسالة عملية قائمة انتظار واحدة على الأقل وعملية فصل واحدة. علاوة على ذلك، تتطلب دلالات قائمة الانتظار عموماً نوعاً من التأمين داخل البنية الأساسية للرسائل. إذا كانت قائمة الانتظار خدمة مُدارة، فقد يكون هناك زمن انتقال إضافي، لأن قائمة الانتظار خارجية بالنسبة للشبكة الظاهرية للمجموعة. يمكنك التخفيف من هذه المشكلات عن طريق تجميع الرسائل، ولكن هذا يعقد التعليمة البرمجية. إذا كانت الرسائل لا تتطلب دلالات قائمة الانتظار، فقد تتمكن من استخدام دفق حدث بدلاً من قائمة انتظار. لمزيد من المعلومات، راجع النمط البنيوي المستند إلى الأحداث.

Drone Delivery: اختيار أنماط الرسائل

يستخدم هذا الحل مثال تسليم الطائرات بدون طيار. إنه مثالي للصناعات الجوية والطائرات.

مع وضع هذه الاعتبارات في الاعتبار، اتخذ فريق التطوير خيارات التصميم التالية لتطبيق تسليم الطائرات بدون طيار:

  • تعرض خدمة البث واجهة برمجة تطبيقات REST عامة تستخدمها تطبيقات العميل لجدولة عمليات التسليم أو تحديثها أو إلغائها.

  • تستخدم خدمة العرض "مراكز الأحداث" لإرسال رسائل غير متزامنة إلى خدمة "المجدول". تعتبر الرسائل غير المتزامنة ضرورية لتنفيذ تسوية التحميل المطلوبة للاستيعاب.

  • تعرض خدمات الحساب، والتسليم، والحزمة، وDrone، والنقل من طرف ثالث واجهات برمجة تطبيقات REST الداخلية. تستدعي خدمة "المجدول" واجهات برمجة التطبيقات هذه لتنفيذ طلب المستخدم. أحد أسباب استخدام واجهات برمجة التطبيقات المتزامنة هو أن المجدول يحتاج إلى الحصول على استجابة من كل خدمة من خدمات المصب. فشل في أي من خدمات المصب يعني فشل العملية بأكملها. ومع ذلك، فإن المشكلة المحتملة هي مقدار زمن الوصول الذي يتم تقديمه من خلال استدعاء خدمات الواجهة الخلفية.

  • إذا كانت أي خدمة متلقية للمعلومات بها فشل غير عابر، فيجب وضع علامة على العملية بأكملها على أنها فاشلة. لمعالجة هذه الحالة، ترسل خدمة المجدول رسالة غير متزامنة إلى المشرف، بحيث يمكن للمشرف جدولة حركات التعويض.

  • تكشف خدمة التسليم عن واجهة برمجة تطبيقات عامة يمكن للعملاء استخدامها للحصول على حالة التسليم. في مقالة بوابة واجهة برمجة التطبيقات، نناقش كيف يمكن لبوابة واجهة برمجة التطبيقات إخفاء الخدمات الأساسية عن العميل، بحيث لا يحتاج العميل إلى معرفة الخدمات التي تكشف عن واجهات برمجة التطبيقات.

  • أثناء تحليق طائرة بدون طيار، ترسل خدمة الطائرات بدون طيار الأحداث التي تحتوي على موقع الطائرة بدون طيار وحالتها الحالية. تستمع خدمة التوصيل إلى هذه الأحداث لتتبع حالة التسليم.

  • عندما تتغير حالة التسليم، ترسل خدمة التسليم حدث حالة التسليم، مثل DeliveryCreated أو DeliveryCompleted. يمكن لأي خدمة الاشتراك في هذه الأحداث. في التصميم الحالي، خدمة "تاريخ التسليم" هي المشترك الوحيد، ولكن قد يكون هناك مشتركون آخرون لاحقاً. على سبيل المثال، قد تنتقل الأحداث إلى خدمة تحليلات في الوقت الحقيقي. ولأن "المجدول" ليس مضطراً إلى انتظار استجابة، فإن إضافة المزيد من المشتركين لا يؤثر على مسار سير العمل الرئيسي.

رسم تخطيطي لاتصال الطائرات بدون طيار

لاحظ أن أحداث حالة التسليم مستمدة من أحداث موقع الطائرة بدون طيار. على سبيل المثال، عندما تصل طائرة بدون طيار إلى موقع تسليم وتسقط حزمة، تترجم خدمة التوصيل هذا إلى حدث DeliveryCompleted. هذا مثال على التفكير من حيث نماذج المجال. كما هو موضح سابقاً، تنتمي إدارة الطائرات بدون طيار إلى سياق محدود منفصل. تنقل أحداث الطائرات بدون طيار الموقع المادي للطائرة بدون طيار. من ناحية أخرى، تمثل أحداث التسليم تغييرات في حالة التسليم، وهو كيان تجاري مختلف.

باستخدام شبكة الخدمة

شبكة الخدمة عبارة عن طبقة برمجية تتعامل مع الاتصال من خدمة إلى خدمة. تم تصميم شبكات الخدمة لوضع حلول للعديد من المخاوف المدرجة في القسم السابق، ونقل المسؤولية عن هذه المخاوف بعيداً عن الخدمات الصغيرة نفسها إلى طبقة مشتركة. تعمل شبكة الخدمة كوكيل proxy يعترض اتصال الشبكة بين الخدمات الصغيرة في المجموعة. حالياً، ينطبق مفهوم شبكة الخدمة بشكل أساسي على منظمي الحاويات، بدلاً من البنى بلا خادم.

ملاحظة

شبكة الخدمة هي مثال على نمط السفير - خدمة مساعدة ترسل طلبات الشبكة نيابة عن التطبيق.

في الوقت الحالي، الخيارات الرئيسية لشبكة الخدمة في Kubernetes هي LinkerdوEstio. كل من هذه التقنيات تتطور بسرعة. ومع ذلك، تتضمن بعض الميزات التي تشترك فيها كل من Linkerd و Istio ما يلي:

  • موازنة الحمل على مستوى الجلسة، بناءً على فترات الاستجابة الملحوظة أو عدد الطلبات المعلقة. يمكن أن يؤدي ذلك إلى تحسين الأداء عبر موازنة حمل الطبقة 4 التي توفرها Kubernetes.

  • يعتمد توجيه الطبقة 7 على مسار URL أو عنوان المضيف أو إصدار API أو قواعد أخرى على مستوى التطبيق.

  • إعادة محاولة الطلبات الفاشلة. تتفهم شبكة الخدمة رموز خطأ HTTP، ويمكنها إعادة محاولة الطلبات الفاشلة تلقائياً. يمكنك تكوين هذا الحد الأقصى لعدد المحاولات، جنباً إلى جنب مع فترة المهلة لتقييد الحد الأقصى لوقت الاستجابة.

  • كسر الدائرة. إذا فشل مثيل في الطلبات باستمرار، فستقوم شبكة الخدمة بتمييزها مؤقتاً على أنها غير متاحة. بعد فترة التراجع، سيحاول المثيل مرة أخرى. يمكنك تكوين قاطع الدائرة بناءً على معايير مختلفة، مثل عدد حالات الفشل المتتالية،

  • تلتقط شبكة الخدمة قياسات حول المكالمات بين الخدمات، مثل حجم الطلب ووقت الاستجابة ومعدلات الخطأ والنجاح وأحجام الاستجابة. تتيح شبكة الخدمة أيضاً التتبع الموزع عن طريق إضافة معلومات الارتباط لكل قفزة في الطلب.

  • مصادقة TLS المتبادلة للمكالمات من خدمة إلى خدمة.

هل تحتاج شبكة خدمة؟ يعتمد ذلك على الحالة. بدون شبكة خدمة، ستحتاج إلى النظر في كل من التحديات المذكورة في بداية هذه المقالة. يمكنك حل مشكلات مثل إعادة المحاولة وقاطع الدائرة والتتبع الموزع بدون شبكة خدمة، لكن شبكة الخدمة تنقل هذه الاهتمامات خارج الخدمات الفردية إلى طبقة مخصصة. من ناحية أخرى، تضيف شبكة الخدمة تعقيداً إلى إعداد المجموعة وتكوينها. قد تكون هناك آثار على الأداء، لأنه يتم توجيه الطلبات الآن من خلال وكيل شبكة الخدمة، ولأن الخدمات الإضافية تعمل الآن على كل عقدة في المجموعة. يجب عليك إجراء اختبار أداء شامل وتحميل قبل توزيع شبكة خدمة في الإنتاج.

عمليات موزعة

يتمثل أحد التحديات الشائعة في الخدمات الصغيرة في معالجة العمليات التي تغطي خدمات متعددة بشكل صحيح. غالباً في هذا السيناريو، إما يكون نجاح العملية مائة في المائة أو الإخفاق؛ بحيث إذا فشلت إحدى الخدمات المشاركة، يجب أن تفشل العملية بأكملها.

هناك حالتان للنظر فيهما:

  • قد تواجه الخدمة إخفاقاً عابراً مثل انتهاء مهلة الشبكة. غالباً ما يمكن حل هذه الأخطاء ببساطة عن طريق إعادة محاولة الاتصال. إذا استمرت العملية بالفشل بعد عدد معين من المحاولات، فسيتم اعتبارها فاشلة غير عابرة.

  • الفشل غير العابر هو أي فشل من غير المحتمل أن يزول من تلقاء نفسه. تشمل حالات الفشل غير العابرة حالات الخطأ العادية، مثل الإدخال غير الصحيح. كما تتضمن أيضاً استثناءات لم تتم معالجتها في تعليمة برمجية التطبيق أو تعطل العملية. في حالة حدوث هذا النوع من الخطأ، يجب وضع علامة على العملية التجارية بأكملها على أنها فاشلة. قد يكون من الضروري التراجع عن خطوات أخرى في نفس العملية التي نجحت بالفعل.

بعد فشل غير مؤقت، قد تكون العملية الحالية في حالة فاشلة جزئياً، حيث تم إكمال خطوة واحدة أو أكثر بالفعل بنجاح. على سبيل المثال، إذا كانت خدمة الطائرة بدون طيار قد حددت بالفعل طائرة بدون طيار، فيجب إلغاء الطائرة بدون طيار. في هذه الحالة، يحتاج التطبيق إلى التراجع عن الخطوات التي نجحت، باستخدام عملية التعويض. في بعض الحالات، يجب أن يتم ذلك عن طريق نظام خارجي أو حتى عن طريق عملية يدوية.

إذا كان منطق تعويض العمليات معقداً، ففكر في إنشاء خدمة منفصلة تكون مسؤولة عن هذه العملية. في تطبيق Drone Delivery، تضع خدمة المجدول العمليات الفاشلة في قائمة انتظار مخصصة. خدمة صغيرة منفصلة، تسمى المشرف، تقرأ من قائمة الانتظار هذه وتستدعي واجهة برمجة تطبيقات للإلغاء على الخدمات التي تحتاج إلى التعويض. هذا تباين في نمط مشرف عامل المجدول. قد تتخذ خدمة المشرف إجراءات أخرى أيضاً، مثل إخطار المستخدم عن طريق رسالة نصية أو بريد إلكتروني، أو إرسال تنبيه إلى لوحة معلومات العمليات.

رسم تخطيطي يوضح الخدمة المصغرة للمشرف

قد تفشل خدمة المجدول نفسها (على سبيل المثال، بسبب تعطل العقدة). في هذه الحالة، يمكن لمثيل جديد أن يدور ويتولى المهمة. ومع ذلك، يجب استئناف أي عمليات كانت قيد التقدم بالفعل.

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

خيار آخر هو تصميم جميع العمليات لتكون عاطلة عن العمل. تكون العملية خاملة إذا كان من الممكن استدعاؤها عدة مرات دون إحداث آثار جانبية إضافية بعد المكالمة الأولى. بشكل أساسي، يجب أن تتجاهل الخدمة المتلقية للمعلومات المكالمات المكررة، ما يعني أن الخدمة يجب أن تكون قادرة على اكتشاف المكالمات المكررة. ليس من السهل دائماً تنفيذ الأساليب المثبطة. لمزيد من المعلومات، راجع العمليات الخاملة.

الخطوات التالية

بالنسبة للخدمات الصغيرة التي تتحدث مباشرة مع بعضها، من المهم إنشاء واجهات برمجة تطبيقات جيدة التصميم.