مقدمة إلى Python Microservices مع Nameko
نشرت: 2022-03-11مقدمة
يعد النمط المعماري للخدمات المصغرة أسلوبًا معماريًا يزداد شعبيته نظرًا لمرونته ومرونته. جنبًا إلى جنب مع تقنيات مثل Kubernetes ، أصبح من السهل تشغيل تطبيق باستخدام بنية Microservices كما لم يحدث من قبل.
وفقًا لمقال كلاسيكي من مدونة Martin Fowler ، يمكن تلخيص الأسلوب المعماري Microservices على النحو التالي:
باختصار ، يعد النمط المعماري للخدمة المصغرة أسلوبًا لتطوير تطبيق واحد كمجموعة من الخدمات الصغيرة ، كل منها يعمل في عمليته الخاصة ويتواصل مع آليات خفيفة الوزن ، غالبًا ما تكون واجهة برمجة تطبيقات لموارد HTTP. تم بناء هذه الخدمات حول إمكانات العمل ويمكن نشرها بشكل مستقل بواسطة آلية نشر مؤتمتة بالكامل.
بمعنى آخر ، يتكون التطبيق الذي يتبع بنية الخدمات المصغرة من عدة خدمات مستقلة وديناميكية تتواصل مع بعضها البعض باستخدام بروتوكول اتصال. من الشائع استخدام HTTP (و REST) ، ولكن كما سنرى ، يمكننا استخدام أنواع أخرى من بروتوكولات الاتصال مثل RPC (استدعاء الإجراء البعيد) عبر AMQP (بروتوكول وضع الرسائل في قائمة انتظار المتقدم).
يمكن اعتبار نمط الخدمات المصغرة كحالة محددة لـ SOA (بنية موجهة للخدمة). في SOA ، من الشائع ، مع ذلك ، استخدام ESB (ناقل خدمة المؤسسة) لإدارة الاتصال بين الخدمات. عادةً ما تكون ESBs معقدة للغاية وتتضمن وظائف لتوجيه الرسائل المعقدة وتطبيق قواعد العمل. في الخدمات المصغرة ، من الشائع أكثر استخدام نهج بديل: "نقاط النهاية الذكية والأنابيب الغبية" ، مما يعني أن الخدمات نفسها يجب أن تحتوي على كل منطق الأعمال والتعقيد (تماسك عالٍ) ، ولكن يجب أن يكون الاتصال بين الخدمات بسيطًا مثل ممكن (فصل عالٍ) ، مما يعني أن الخدمة لا تحتاج بالضرورة إلى معرفة الخدمات الأخرى التي ستتواصل معها. هذا فصل من الاهتمامات المطبقة على المستوى المعماري.
جانب آخر من الخدمات المصغرة هو أنه لا يوجد إنفاذ بشأن التقنيات التي يجب استخدامها في كل خدمة. يجب أن تكون قادرًا على كتابة خدمة مع أي حزمة برامج يمكنها الاتصال بالخدمات الأخرى. كل خدمة لها إدارة دورة حياتها الخاصة أيضًا. كل هذا يعني أنه في الشركة ، من الممكن أن يكون هناك فرق تعمل على خدمات منفصلة ، باستخدام تقنيات مختلفة وحتى منهجيات الإدارة. سيهتم كل فريق بقدرات العمل ، مما يساعد على بناء منظمة أكثر مرونة.
خدمات Python المصغرة
مع وضع هذه المفاهيم في الاعتبار ، سنركز في هذه المقالة على بناء دليل على تطبيق Microservices للمفهوم باستخدام Python. لذلك ، سوف نستخدم Nameko ، وهو إطار عمل خدمات مصغرة لـ Python. يحتوي على RPC عبر AMQP مدمج ، مما يسمح لك بالتواصل بسهولة بين خدماتك. يحتوي أيضًا على واجهة بسيطة لاستعلامات HTTP ، والتي سنستخدمها في هذا البرنامج التعليمي. ومع ذلك ، لكتابة Microservices التي تعرض نقطة نهاية HTTP ، يوصى باستخدام إطار عمل آخر ، مثل Flask. لاستدعاء طرق Nameko عبر RPC باستخدام Flask ، يمكنك استخدام flask_nameko ، وهو غلاف مصمم فقط للتشغيل البيني Flask مع Nameko.
إعداد البيئة الأساسية
لنبدأ بتشغيل أبسط مثال ممكن ، مستخرج من موقع Nameko على الويب ، وتوسيعه لأغراضنا. أولاً ، ستحتاج إلى تثبيت Docker. سنستخدم Python 3 في أمثلةنا ، لذا تأكد من تثبيتها أيضًا. بعد ذلك ، قم بإنشاء python virtualenv وتشغيل $ pip install nameko
.
لتشغيل Nameko ، نحتاج إلى وسيط الرسائل RabbitMQ. سيكون مسؤولاً عن الاتصال بين خدمات Nameko الخاصة بنا. لا تقلق ، لأنك لست بحاجة إلى تثبيت تبعية أخرى على جهازك. باستخدام Docker ، يمكننا ببساطة تنزيل صورة معدة مسبقًا ، وتشغيلها ، وعندما ننتهي ، قم ببساطة بإيقاف الحاوية. لا توجد شياطين أو تثبيت apt-get
أو dnf install
.
ابدأ حاوية RabbitMQ بتشغيل $ docker run -p 5672:5672 --hostname nameko-rabbitmq rabbitmq:3
(قد تحتاج إلى sudo للقيام بذلك). سيبدأ هذا حاوية Docker باستخدام أحدث إصدار 3 RabbitMQ ويعرضها عبر المنفذ الافتراضي 5672.
مرحبًا بالعالم مع الخدمات المصغرة
انطلق وأنشئ ملفًا يسمى hello.py
بالمحتوى التالي:
from nameko.rpc import rpc class GreetingService: name = "greeting_service" @rpc def hello(self, name): return "Hello, {}!".format(name)
خدمات Nameko عبارة عن فئات. تعرض هذه الفئات نقاط الدخول ، والتي يتم تنفيذها كملحقات. تتضمن الإضافات المضمنة القدرة على إنشاء نقاط دخول تمثل أساليب RPC أو أدوات استماع الأحداث أو نقاط نهاية HTTP أو أجهزة ضبط الوقت. هناك أيضًا امتدادات مجتمعية يمكن استخدامها للتفاعل مع قاعدة بيانات PostgreSQL و Redis وما إلى ذلك ... من الممكن كتابة الامتدادات الخاصة بك.
دعنا نمضي قدمًا وننفذ مثالنا. إذا قمت بتشغيل RabbitMQ على المنفذ الافتراضي ، فما عليك سوى تشغيل $ nameko run hello
. سيجد RabbitMQ ويتصل به تلقائيًا. بعد ذلك ، لاختبار خدمتنا ، قم بتشغيل $ nameko shell
في محطة طرفية أخرى. سيؤدي ذلك إلى إنشاء غلاف تفاعلي يتصل بنفس مثيل RabbitMQ. الشيء العظيم ، باستخدام RPC عبر AMQP ، يقوم Nameko بتنفيذ اكتشاف الخدمة تلقائيًا. عند استدعاء طريقة RPC ، سيحاول nameko العثور على خدمة التشغيل المقابلة.
عند تشغيل Nameko shell ، ستحصل على كائن خاص يسمى n
مضافًا إلى مساحة الاسم. يسمح هذا الكائن بإرسال الأحداث وإجراء مكالمات RPC. لإجراء مكالمة RPC لخدمتنا ، قم بتشغيل:
> >> n.rpc.greetingservice.hello(name='world') 'Hello, world!'
المكالمات المتزامنة
يتم إنشاء مثيل لفئات الخدمة هذه في اللحظة التي يتم فيها إجراء مكالمة وإتلافها بعد اكتمال المكالمة. لذلك ، يجب أن يكونوا عديمي الجنسية بطبيعتهم ، مما يعني أنه يجب ألا تحاول الاحتفاظ بأي حالة في الكائن أو الفئة بين المكالمات. هذا يعني أن الخدمات نفسها يجب أن تكون عديمة الجنسية. مع افتراض أن جميع الخدمات عديمة الحالة ، فإن Nameko قادر على الاستفادة من التزامن عن طريق استخدام greenthreads للحدث الصغير. تسمى الخدمات التي تم إنشاء مثيل لها "العمال" ، ويمكن أن يكون هناك حد أقصى مهيأ لعدد العمال الذين يعملون في نفس الوقت.
للتحقق من تزامن Nameko في الممارسة العملية ، قم بتعديل الكود المصدري عن طريق إضافة وضع السكون إلى استدعاء الإجراء قبل إعادة الرد:
from time import sleep from nameko.rpc import rpc class GreetingService: name = "greeting_service" @rpc def hello(self, name): sleep(5) return "Hello, {}!".format(name)
نحن نستخدم وضع sleep
من وحدة time
، وهي غير مفعلة غير متزامنة. ومع ذلك ، عند تشغيل خدماتنا باستخدام nameko run
، فسيتم تلقائيًا تصحيح عوائد المشغلات من حظر المكالمات مثل sleep(5)
.
من المتوقع الآن أن يستغرق وقت الاستجابة من استدعاء إجراء حوالي 5 ثوانٍ. ومع ذلك ، ماذا سيكون السلوك من المقتطف التالي ، عندما نقوم بتشغيله من nameko shell؟
res = [] for i in range(5): hello_res = n.rpc.greeting_service.hello.call_async(name=str(i)) res.append(hello_res) for hello_res in res: print(hello_res.result())
يوفر Nameko طريقة call_async
غير محظورة لكل نقطة إدخال RPC ، ويعيد كائن رد وكيل يمكن الاستعلام عنه بعد ذلك عن نتائجه. سيتم حظر طريقة result
، عند استدعائها في وكيل الرد ، حتى يتم إرجاع الرد.
كما هو متوقع ، يتم تشغيل هذا المثال في حوالي خمس ثوانٍ فقط. سيتم حظر كل عامل في انتظار انتهاء مكالمة sleep
، لكن هذا لا يمنع عامل آخر من البدء. استبدل مكالمة sleep
هذه بمكالمة قاعدة بيانات I / O مفيدة للحظر ، على سبيل المثال ، وستحصل على خدمة متزامنة سريعة للغاية.
كما أوضحنا سابقًا ، ينشئ Nameko عمالًا عند استدعاء طريقة. يمكن تكوين الحد الأقصى لعدد العمال. بشكل افتراضي ، يتم تعيين هذا الرقم على 10. يمكنك اختبار تغيير range(5)
في المقتطف أعلاه ، على سبيل المثال ، النطاق (20). سيؤدي هذا إلى استدعاء طريقة hello
20 مرة ، والتي يجب أن تستغرق الآن عشر ثوان للتشغيل:
> >> res = [] > >> for i in range(20): ... hello_res = n.rpc.greeting_service.hello.call_async(name=str(i)) ... res.append(hello_res) > >> for hellores in res: ... print(hello_res.result()) Hello, 0! Hello, 1! Hello, 2! Hello, 3! Hello, 4! Hello, 5! Hello, 6! Hello, 7! Hello, 8! Hello, 9! Hello, 10! Hello, 11! Hello, 12! Hello, 13! Hello, 14! Hello, 15! Hello, 16! Hello, 17! Hello, 18! Hello, 19!
الآن ، افترض أنك حصلت على عدد كبير جدًا (أكثر من 10) من المستخدمين المتزامنين الذين يتصلون بطريقة hello
هذه. سيعلق بعض المستخدمين الانتظار أكثر من الخمس ثوانٍ المتوقعة للاستجابة. كان أحد الحلول هو زيادة عدد الأعمال عن طريق تجاوز الإعدادات الافتراضية باستخدام ملف التكوين ، على سبيل المثال. ومع ذلك ، إذا كان الخادم الخاص بك بالفعل في حدوده مع هؤلاء العمال العشرة لأن الطريقة التي تم استدعاؤها تعتمد على بعض استعلامات قاعدة البيانات الثقيلة ، فإن زيادة عدد العمال قد يؤدي إلى زيادة وقت الاستجابة بشكل أكبر.
توسيع نطاق خدمتنا
الحل الأفضل هو استخدام قدرات Nameko Microservices. حتى الآن ، استخدمنا خادمًا واحدًا فقط (جهاز الكمبيوتر الخاص بك) ، وقمنا بتشغيل مثيل واحد من RabbitMQ ومثيل واحد للخدمة. في بيئة الإنتاج ، سترغب في زيادة عدد العقد التي تقوم بتشغيل الخدمة التي تتلقى عددًا كبيرًا جدًا من المكالمات بشكل تعسفي. يمكنك أيضًا بناء مجموعة RabbitMQ إذا كنت تريد أن يكون وسيط الرسائل الخاص بك أكثر موثوقية.
لمحاكاة توسيع نطاق الخدمة ، يمكننا ببساطة فتح محطة طرفية أخرى وتشغيل الخدمة كما في السابق ، باستخدام $ nameko run hello
. سيبدأ هذا مثيل خدمة آخر مع إمكانية تشغيل عشرة عمال آخرين. الآن ، حاول تشغيل هذا المقتطف مرة أخرى باستخدام range(20)
. يجب أن يستغرق الأمر الآن خمس ثوانٍ مرة أخرى للتشغيل. عندما يكون هناك أكثر من مثيلات خدمة واحدة قيد التشغيل ، سيقوم Nameko بتدوير طلبات RPC بين المثيلات المتاحة.
تم تصميم Nameko للتعامل بقوة مع استدعاءات هذه الأساليب في الكتلة. لاختبار ذلك ، حاول تشغيل الجهاز المقتطع وقبل أن ينتهي ، انتقل إلى أحد الأجهزة الطرفية التي تقوم بتشغيل خدمة Nameko واضغط على Ctrl+C
مرتين. سيؤدي هذا إلى إغلاق المضيف دون انتظار انتهاء العمال. سيعيد Nameko تخصيص المكالمات إلى مثيل خدمة آخر متاح.
من الناحية العملية ، ستستخدم Docker لتجميع خدماتك في حاويات ، كما سنفعل لاحقًا ، وأداة تنسيق مثل Kubernetes لإدارة العقد التي تشغل الخدمة والاعتمادات الأخرى ، مثل وسيط الرسائل. إذا تم إجراؤه بشكل صحيح ، باستخدام Kubernetes ، فستحول تطبيقك بشكل فعال إلى نظام موزع قوي ، محصن ضد القمم غير المتوقعة. أيضًا ، يتيح Kubernetes عمليات نشر خالية من التعطل. لذلك ، لن يؤثر نشر إصدار جديد من الخدمة على توفر نظامك.

من المهم إنشاء خدمات مع مراعاة بعض التوافق مع الإصدارات السابقة ، حيث يمكن أن يحدث ذلك في بيئة الإنتاج لعدة إصدارات مختلفة من نفس الخدمة ليتم تشغيلها في نفس الوقت ، خاصة أثناء النشر. إذا كنت تستخدم Kubernetes ، فلن يؤدي أثناء النشر إلا إلى قتل جميع حاويات الإصدار القديم عندما يكون هناك ما يكفي من الحاويات الجديدة قيد التشغيل.
بالنسبة إلى Nameko ، لا يمثل وجود عدة إصدارات مختلفة من نفس الخدمة تعمل في نفس الوقت مشكلة. نظرًا لأنه يوزع المكالمات بطريقة مستديرة ، فقد تمر المكالمات بإصدارات قديمة أو جديدة. لاختبار ذلك ، احتفظ بمحطة واحدة مع خدمتنا تعمل بالإصدار القديم ، وقم بتحرير وحدة الخدمة لتبدو كما يلي:
from time import sleep from nameko.rpc import rpc class GreetingService: name = "greeting_service" @rpc def hello(self, name): sleep(5) return "Hello, {}! (version 2)".format(name)
إذا قمت بتشغيل هذه الخدمة من محطة طرفية أخرى ، فسيتم تشغيل النسختين في نفس الوقت. الآن ، قم بتشغيل مقتطف الاختبار الخاص بنا مرة أخرى وسترى كلا الإصدارين معروضين:
> >> res = [] > >> for i in range(5): ... hello_res = n.rpc.greeting_service.hello.call_async(name=str(i)) ... res.append(hello_res) > >> for hellores in res: ... print(hello_res.result()) Hello, 0! Hello, 1! (version 2) Hello, 2! Hello, 3! (version 2) Hello, 4!
العمل مع مثيلات متعددة
الآن نحن نعرف كيفية العمل بفعالية مع Nameko ، وكيف يعمل القياس. لنأخذ الآن خطوة إلى الأمام ونستخدم المزيد من الأدوات من نظام Docker البيئي: docker-compose. سيعمل هذا إذا كنت تقوم بالنشر على خادم واحد ، وهو بالتأكيد ليس مثاليًا لأنك لن تستفيد من العديد من مزايا بنية الخدمات المصغرة. مرة أخرى ، إذا كنت ترغب في الحصول على بنية تحتية أكثر ملاءمة ، فيمكنك استخدام أداة التزامن مثل Kubernetes لإدارة نظام موزع للحاويات. لذا ، انطلق وقم بتثبيت docker-compose.
مرة أخرى ، كل ما يتعين علينا القيام به هو نشر مثيل RabbitMQ وسيتولى Nameko الاهتمام بالباقي ، نظرًا لأن جميع الخدمات يمكنها الوصول إلى مثيل RabbitMQ هذا. كود المصدر الكامل لهذا المثال متاح في مستودع GitHub هذا.
لنقم ببناء تطبيق سفر بسيط لاختبار قدرات Nameko. يسمح هذا التطبيق بتسجيل المطارات والرحلات. يتم تخزين كل مطار على أنه اسم المطار ، وتقوم الرحلة بتخزين معرفات مطارات الأصل والمطار. تبدو بنية نظامنا كما يلي:
من الناحية المثالية ، سيكون لكل خدمة مصغرة مثيل قاعدة البيانات الخاصة بها. ومع ذلك ، من أجل التبسيط ، قمت بإنشاء قاعدة بيانات Redis واحدة لكل من الخدمات المصغرة للرحلات والمطارات للمشاركة. ستتلقى الخدمة المصغرة للبوابة طلبات HTTP عبر واجهة برمجة تطبيقات بسيطة تشبه REST وتستخدم RPC للتواصل مع المطارات والرحلات.
لنبدأ بالخدمة المصغرة للبوابة. هيكلها واضح ومباشر ويجب أن يكون مألوفًا جدًا لأي شخص يأتي من إطار مثل Flask. نحدد أساسًا نقطتي نهاية ، تسمح كل منهما بطريقتي GET و POST:
import json from nameko.rpc import RpcProxy from nameko.web.handlers import http class GatewayService: name = 'gateway' airports_rpc = RpcProxy('airports_service') trips_rpc = RpcProxy('trips_service') @http('GET', '/airport/<string:airport_id>') def get_airport(self, request, airport_id): airport = self.airports_rpc.get(airport_id) return json.dumps({'airport': airport}) @http('POST', '/airport') def post_airport(self, request): data = json.loads(request.get_data(as_text=True)) airport_id = self.airports_rpc.create(data['airport']) return airport_id @http('GET', '/trip/<string:trip_id>') def get_trip(self, request, trip_id): trip = self.trips_rpc.get(trip_id) return json.dumps({'trip': trip}) @http('POST', '/trip') def post_trip(self, request): data = json.loads(request.get_data(as_text=True)) trip_id = self.trips_rpc.create(data['airport_from'], data['airport_to']) return trip_id
دعونا نلقي نظرة على خدمة المطارات الآن. كما هو متوقع ، فإنه يعرض طريقتين RPC. سيستعلم التابع get
ببساطة عن قاعدة بيانات Redis ويعيد المطار للمعرّف المحدد. سينشئ التابع create
معرّفًا عشوائيًا ، ويخزن معلومات المطار ، ويعيد المعرّف:
import uuid from nameko.rpc import rpc from nameko_redis import Redis class AirportsService: name = "airports_service" redis = Redis('development') @rpc def get(self, airport_id): airport = self.redis.get(airport_id) return airport @rpc def create(self, airport): airport_id = uuid.uuid4().hex self.redis.set(airport_id, airport) return airport_id
لاحظ كيف نستخدم الامتداد nameko_redis
. ألق نظرة على قائمة ملحقات المجتمع. يتم تنفيذ الامتدادات بطريقة تستخدم حقن التبعية. يعتني Nameko ببدء كائن الامتداد الفعلي الذي سيستخدمه كل عامل.
لا يوجد فرق كبير بين خدمات المطارات والخدمات المصغرة للرحلات. إليك كيف ستبدو الخدمة المصغرة لـ Trips:
import uuid from nameko.rpc import rpc from nameko_redis import Redis class AirportsService: name = "trips_service" redis = Redis('development') @rpc def get(self, trip_id): trip = self.redis.get(trip_id) return trip @rpc def create(self, airport_from_id, airport_to_id): trip_id = uuid.uuid4().hex self.redis.set(trip_id, { "from": airport_from_id, "to": airport_to_id }) return trip_id
يعد Dockerfile
لكل خدمة مصغرة واضحًا جدًا أيضًا. التبعية الوحيدة هي nameko
، وفي حالة خدمات المطارات والرحلات ، هناك حاجة لتثبيت nameko-redis
أيضًا. يتم توفير تلك التبعيات في requirements.txt
في كل خدمة. يبدو ملف Dockerfile الخاص بخدمة المطارات كما يلي:
FROM python:3 RUN apt-get update && apt-get -y install netcat && apt-get clean WORKDIR /app COPY requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt COPY config.yml ./ COPY run.sh ./ COPY airports.py ./ RUN chmod +x ./run.sh CMD ["./run.sh"]
الاختلاف الوحيد بين ذلك وملف Dockerfile للخدمات الأخرى هو الملف المصدر (في هذه الحالة ، airports.py
) ، والذي يجب تغييره وفقًا لذلك.
يعتني البرنامج النصي run.sh
بالانتظار حتى RabbitMQ ، وفي حالة خدمات المطارات والرحلات ، تكون قاعدة بيانات Redis جاهزة. يُظهر المقتطف التالي محتوى run.sh
للمطارات. مرة أخرى ، بالنسبة للخدمات الأخرى ، قم فقط بالتغيير من aiports
إلى gateway
أو trips
وفقًا لذلك:
#!/bin/bash until nc -z ${RABBIT_HOST} ${RABBIT_PORT}; do echo "$(date) - waiting for rabbitmq..." sleep 1 done until nc -z ${REDIS_HOST} ${REDIS_PORT}; do echo "$(date) - waiting for redis..." sleep 1 done nameko run --config config.yml airports
خدماتنا جاهزة الآن للتشغيل:
$ docker-compose up
دعونا نختبر نظامنا. قم بتشغيل الأمر:
$ curl -i -d "{\"airport\": \"first_airport\"}" localhost:8000/airport HTTP/1.1 200 OK Content-Type: text/plain; charset=utf-8 Content-Length: 32 Date: Sun, 27 May 2018 05:05:53 GMT f2bddf0e506145f6ba0c28c247c54629
هذا السطر الأخير هو المعرف الذي تم إنشاؤه لمطارنا. لاختبار ما إذا كان يعمل ، قم بتشغيل:
$curl localhost:8000/airport/f2bddf0e506145f6ba0c28c247c54629 {"airport": "first_airport"} Great, now let's add another airport: $ curl -i -d "{\"airport\": \"second_airport\"}" localhost:8000/airport HTTP/1.1 200 OK Content-Type: text/plain; charset=utf-8 Content-Length: 32 Date: Sun, 27 May 2018 05:06:00 GMT 565000adcc774cfda8ca3a806baec6b5
الآن لدينا مطاران ، هذا يكفي لتكوين رحلة. لنقم بإنشاء رحلة الآن:
$ curl -i -d "{\"airport_from\": \"f2bddf0e506145f6ba0c28c247c54629\", \"airport_to\": \"565000adcc774cfda8ca3a806baec6b5\"}" localhost:8000/trip HTTP/1.1 200 OK Content-Type: text/plain; charset=utf-8 Content-Length: 32 Date: Sun, 27 May 2018 05:09:10 GMT 34ca60df07bc42e88501178c0b6b95e4
كما كان من قبل ، يمثل هذا السطر الأخير معرف الرحلة. دعنا نتحقق مما إذا تم إدخاله بشكل صحيح:
$ curl localhost:8000/trip/34ca60df07bc42e88501178c0b6b95e4 {"trip": "{'from': 'f2bddf0e506145f6ba0c28c247c54629', 'to': '565000adcc774cfda8ca3a806baec6b5'}"}
ملخص
لقد رأينا كيف يعمل Nameko من خلال إنشاء مثيل تشغيل محلي لـ RabbitMQ ، والاتصال به وإجراء عدة اختبارات. بعد ذلك ، طبقنا المعرفة المكتسبة لإنشاء نظام بسيط باستخدام بنية الخدمات المصغرة.
على الرغم من بساطته الشديدة ، فإن نظامنا قريب جدًا مما سيبدو عليه النشر الجاهز للإنتاج. يفضل استخدام إطار عمل آخر للتعامل مع طلبات HTTP مثل Falcon أو Flask. كلاهما خياران رائعان ويمكن استخدامهما بسهولة لإنشاء خدمات مصغرة أخرى تعتمد على HTTP ، في حالة رغبتك في كسر خدمة البوابة ، على سبيل المثال. يتمتع Flask بميزة وجود مكون إضافي بالفعل للتفاعل مع Nameko ، ولكن يمكنك استخدام nameko-proxy مباشرة من أي إطار عمل.
من السهل جدًا اختبار Nameko أيضًا. لم نقم بتغطية الاختبار هنا من أجل البساطة ، ولكن تحقق من وثائق اختبار Nameko.
مع جميع الأجزاء المتحركة داخل بنية الخدمات المصغرة ، فأنت تريد التأكد من حصولك على نظام تسجيل قوي. لإنشاء واحدة ، راجع Python Logging: An-Depth Tutorial بواسطة زميل Toptaler و Python Developer: Son Nguyen Kim.