الألم والمعاناة عند تصحيح الخدمات الصغيرة في تطوير الويب

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

في هذا المنشور ، يشارك زملاء من ممارسة جافا في Rexoft تجاربهم حول كيفية تصحيح الخدمات الصغيرة للويب.



كيفية تحقيق تكامل بيانات المعاملات


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

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

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

المشكلة الرئيسية مع microservices هي أنها سهلة للغاية لتشغيل محليا (على سبيل المثال، وذلك باستخدام spring.io و فكرة IntelliJ ، وهذا يمكن أن يتم في 5 دقائق فقط، أو حتى أقل من ذلك). ومع ذلك ، عند محاولة القيام بنفس الشيء في Kubernetesالكتلة (إذا كانت لديك خبرة قليلة معها من قبل) ، فإن الإطلاق البسيط لوحدة التحكم بطباعة "Hello World" عند الوصول إلى نقطة نهاية معينة قد يستغرق نصف يوم. في حالة monolith ، يكون الوضع أبسط. لكل مطور خادم تطبيقات محلي. عملية النشر بسيطة للغاية أيضًا - تحتاج إلى نسخ قطعة الحرب / الأذن النهائية إلى المكان الصحيح في Application Server يدويًا أو باستخدام IDE . عادة هذه ليست مشكلة.

تصحيح الفروق الدقيقة


النقطة الثانية المهمة هي تصحيح الأخطاء . في المواقف المتراصة ، يفترض أن المطور لديه خادم تطبيقات على جهازه ، حيث يتم نشر حربه / أذنه. يمكنك دائمًا تصحيح الأخطاء ، لأن كل ما تحتاج إليه في متناول اليد. مع الخدمات الصغيرة ، كل شيء يكون أكثر تعقيدًا قليلاً ، وعادة ما تكون الخدمة في حد ذاتها. كقاعدة ، لديه مخطط قاعدة بيانات خاص به ، حيث توجد بياناته ، ويقوم بوظائف محددة خاصة به ، ويتم تنظيم جميع الاتصالات مع الخدمات الأخرى عبر مكالمات HTTP متزامنة (على سبيل المثال عبر RestTemplate أو Feign) ، غير متزامن (مثل Kafka أو RabbitMQ). لذلك ، تصبح المهمة البسيطة في الأساس وهي حفظ أو التحقق من كائن معين تم تنفيذه سابقًا في مكان واحد ، داخل ملف حرب / أذن واحد ، في الحالة العامة باستخدام نهج الخدمات الصغيرة ، يمكن تمثيله في النموذج: انتقل إلى خدمة واحدة أو N الخدمات المجاورة ، سواء كانت عمليات الحصول على البيانات ، على سبيل المثال ، بعض القيم المرجعية ، أو عملية حفظ الكيانات المجاورة ،التي تكون بياناتها مطلوبة لأداء منطق الأعمال في خدمتنا. تصبح كتابة منطق الأعمال في هذه الحالة أكثر صعوبة.

وفقًا لذلك ، تكون خيارات الحل كما يلي :

  1. اكتب كود منطق عملك. في الوقت نفسه ، يتم الاستهزاء بجميع المكالمات الخارجية - تتم محاكاة العقود الخارجية ، ويتم كتابة الاختبارات وفقًا للافتراضات التي تقول بأن العقود الخارجية هي هكذا تمامًا ، بعد ذلك يتم نشرها في الدائرة للتحقق منها. في بعض الأحيان يكون الأمر محظوظًا ويعمل التكامل على الفور ، وأحيانًا يكون ذلك غير محظوظ - يجب عليك إعادة رمز منطق الأعمال لعدد n مرات ، لأنه خلال الوقت الذي قمنا فيه بتنفيذ الوظيفة ، تم تحديث الرمز في الخدمة المجاورة ، وتغيير توقيعات API ونحتاج إلى إعادة ذلك جزء من المهمة على جانبها.
  2. . , , Kubernetes, . . , — , remote debug . , runtime , , . -, , 2–5 , . . , Kubernetes , . -, (Per thread), , .

Kubernetes


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

على الجهاز المحلي ، يقوم المطور بتثبيت telepresenc e ، ويقوم بتكوين kubectl للوصول إلى مجموعة Kubernetes المقابلة (يضيف تكوين الحلقة إلى ~ / .kube / config ). بعد ذلك ، يبدأ التواجد عن بعد ، والذي يعمل في الواقع كوكيل بين كمبيوتر المطور المحلي و Kubernetes. هناك خيارات إطلاق مختلفة ، من الأفضل البحث بمزيد من التفصيل في الدليل الرسمي ، ولكن في الحالة الأساسية ، ينقسم الأمر إلى خطوتين:

  1. Sudo telepresence (, Linux- , sudo . , root/). Kubernetes deployment telepresence . deployment Kubernetes.
  2. بدء مثيل الخدمة كالمعتاد على الكمبيوتر المحلي للمطور. ومع ذلك ، في هذه الحالة ، سيكون بإمكانه الوصول إلى البنية التحتية الكاملة لمجموعة Kubernetes ، سواء كانت Service Discovery (Eureka ، Consul) ، Api Gateway (Zuul) ، Kafka وقوائم الانتظار الخاصة بها ، إن وجدت ، وما إلى ذلك. هذا ، في الواقع ، لدينا إمكانية الوصول إلى كل بيئة المجموعة التي نحتاجها ، ولكن محليًا. المكافأة هي إمكانية التصحيح المحلي ، ولكن في بيئة عنقودية ، وستكون بالفعل أسرع بكثير ، لأننا ، في الواقع ، داخل Kubernetes (من خلال النفق) ، ولا نصل إليها من الخارج عبر منفذ لتصحيح الأخطاء عن بُعد.

هذا الحل له عيوب عديدة:

  1. Telepresence Linux Mac, Windows VFS, , issue GitHub. . , - Linux/Mac, .
  2. , Service Discovery (Eureka, Consul)Round Robin , endpoint , , , :

  • kubernetes -> . telepresence deployment , «» Eureka ip-address:port/service-name dns-name:port/service-name , . . Kubernetes , timeout;
  • deployment - Kubernetes , ( ) (Round Robin), ;
  • endpoint, feature, HTTP 404 endpoint Gateway, Service Discovery , Round Robin . Service Discovery endpoint , HTTP 404.
  • , , .


من خلال التوجيه الديناميكي للطلب ، نعني أن بوابة API (Zuul) لديها القدرة على الاختيار من بين عدة مثيلات للخدمة نفسها التي نحتاجها. في الحالة العامة ، يمكن حل هذه المشكلة عن طريق إضافة تقدير يسمح لك بتحديد الخدمة المطلوبة من تجمع الخدمات المشتركة التي تحمل الاسم نفسه في مرحلة معالجة الطلب. بطبيعة الحال ، كل خدمة من بين تلك التي نريد أن نتمكن من توجيهها ديناميكيًا ، يجب أن يكون لديها نوع من المعلومات الوصفية التي تحتوي على البيانات التي سيتم استخدامها لتحديد ما إذا كانت هذه الخدمة مطلوبة أم لا. Spring Cloud (في حالة Eureka) ، على سبيل المثال ، يتيح لك القيام بذلك عن طريق التحديد في كتلة بيانات وصفية خاصة في application.yml :

eureka:
  instance:
    preferIpAddress: true
    metadata-map:
      service.label: develop

بعد تسجيل مثل هذه الخدمة في Service Discovery في com.netflix.appinfo.InstanceInfo # getMetadata سيكون هناك تسمية مع مفتاح service.label وتطوير القيمة ، والتي يمكن الحصول عليها في وقت التشغيل. تتمثل نقطة مهمة في بداية الخدمة في التحقق مما إذا كان مثيل الخدمة موجودًا في Service Discovery مع هذه المعلومات الوصفية أم لا لتجنب التصادمات المحتملة.

خيارات التوجيه


بعد ذلك ، يمكن تقليل حل المشكلة إلى خيارين:

  1. API Gateway . , , , , Headers: DestionationService: feature/PRJ-001. , , Header . , — - API Gateway.
  2. API Gateway, , . ., , , Zuul 1 endpoint- /api/users/… user, feature/PRJ-001, Zuul 2 endpoint- /api/users/… user, feature/PRJ-002. , N API Gateway N , . . , . . feature — , , , , , , . API Gateway, , . ., , , — , .






كجزء من Gateway API ، من المفيد أيضًا توفير آلية تتيح لك القدرة على تغيير قواعد التوجيه في وقت التشغيل. من الأفضل وضع هذه الإعدادات في خريطة التهيئة . في هذه الحالة ، سيكون كافياً لإعادة كتابة المسارات الجديدة وإما إعادة تشغيل Gateway API في Kubernetes لتحديث التوجيه ، أو استخدام Spring Boot Actuator (بشرط وجود تبعية مقابلة في Gateway API) - نقطة نهاية / تحديث المكالمة ، التي تتم إعادة قراءتها بشكل أساسي البيانات من config-map وسيتم تحديث المسارات.

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

مثال على كتلة خريطة التهيئة لـ Gateway API التي تحتوي على إعدادات للتوجيه (هنا هو مجرد مثال لكيفية ظهورها ، من أجل التشغيل الصحيح ، يتطلب ربطًا مناظرًا في شكل رمز على الجانب الخلفي من خدمة بوابة API) :

{
  "kind": "ConfigMap",
  "apiVersion": "v1",
  "metadata": {
    ...
  },  
"data": {
    ...        
    "rules.meta.user": "develop",
    "rules.meta.client": "develop",
    "rules.meta.notification": "feature/PRJ-010",
    ...    
  }
}

Rules.meta عبارة عن خريطة تحتوي على قواعد التوجيه للخدمات.
المستخدم / العميل / الإخطار - اسم الخدمة التي تم تسجيلها بموجبها في يوريكا.

التطوير / الميزة / PRJ-010 - ملصق الخدمة من application.yml للخدمة المقابلة ، بناءً على ذلك سيتم اختيار الخدمة المطلوبة من بين جميع الخدمات المتاحة التي تحمل نفس الاسم من Service Discovery ، إذا كان هناك أكثر من مثيل واحد لهذه الخدمة.

استنتاج


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

آمل أن تساعدك هذه المواد في حل مشكلتك. مهام مثيرة للاهتمام وبيعها دون أخطاء!

All Articles