تنظيم التفاعل الفعال للخدمات الصغيرة

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



تطبيق عادي


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


معمارية التطبيقات النموذجية

يتكون التطبيق من المكونات التالية:

  • أداة اختبار النظام: jMeter .
  • الخدمة أ: خدمة صغيرة تنفذ طلبات الخدمة ب وتعرض الردود المستلمة منها.
  • الخدمة ب (الخدمة ب): خدمة صغيرة ترسل بيانات JSON الثابتة استجابةً للطلبات بعد تأخير مدته 10 مللي ثانية يتم استخدامه لجميع واجهات برمجة التطبيقات.
  • الأجهزة الظاهرية (VM 1 و VM 2): أمثلة Amazon EC2 t2.xlarge.

▍HTTP / 1.1


HTTP / 1.1 هي تقنية قياسية لتنظيم تفاعل الخدمات الصغيرة ، والتي يتم استخدامها عند استخدام أي مكتبات HTTP مثل المحاور أو الوكلاء الفائقين .

إليك رمز الخدمة B الذي يطبّق واجهة برمجة التطبيقات لنظامنا:

server.route({  method: 'GET',  path: '/',  handler: async (request, h) => {    const response = await new Promise((resolve) => {      setTimeout(() => {        resolve({          id: 1,          name: 'Abhinav Dhasmana',          enjoys_coding: true,        });      }, 10);    });    return h.response(response);  }, }); 

إليك رمز الخدمة A التي تصل إلى الخدمة B باستخدام HTTP / 1.1:

 server.route({ method: 'GET', path: '/', handler: async (request, h) => { try { const response = await Axios({ url: 'http://localhost:8001/', method: 'GET', }); return h.response(response.data); } catch (err) { throw Boom.clientTimeout(err); } }, }); 

من خلال تشغيل هذه الخدمات الصغيرة ، يمكننا الاستفادة من jMeter لتشغيل اختبارات الأداء. سنكتشف كيف يتصرف النظام عند العمل مع 50 مستخدمًا ، كل منهم ينفذ 2000 طلب. كما يتضح من الشكل التالي ، يبلغ متوسط ​​نتائج القياس 37 مللي ثانية.


نتائج البحث من نظام الوضع العادي باستخدام HTTP / 1.1 باستخدام jMeter

RgRPC


يستخدم gRPC تقنية Protocol Buffers بشكل افتراضي. لذلك ، باستخدام gRPC ، بالإضافة إلى رمز خدمتين ، سنحتاج إلى كتابة رمز الملف الأولي ، الذي يصف الواجهة بين وحدات النظام.

 syntax = "proto3"; service SampleDataService { rpc GetSampleData (Empty) returns (SampleData) {} } message SampleData { int32 id = 1; string name = 2; bool enjoys_coding = 3; } message Empty {} 

الآن ، بما أننا نخطط الآن لاستخدام gRPC ، نحتاج إلى إعادة كتابة رمز الخدمة B:

 const grpc = require('grpc'); const proto = grpc.load('serviceB.proto'); const server = new grpc.Server(); const GetSampleData = (call, callback) => { setTimeout(() => {   callback(null, {     id: 1,     name: 'Abhinav Dhasmana',     enjoys_coding: true,   }); }, 10); }; server.addService(proto.SampleDataService.service, { GetSampleData, }); const port = process.env.PORT; console.log('port', port); server.bind(`0.0.0.0:${port}`, grpc.ServerCredentials.createInsecure()); server.start(); console.log('grpc server is running'); 

انتبه لبعض ميزات هذا الرمز:

  • const server = new grpc.Server(); الأمر const server = new grpc.Server(); نقوم بإنشاء خادم grpc.
  • server.addService(proto... الأمر server.addService(proto... نضيف الخدمة إلى الخادم.
  • يتم server.bind(`0.0.0.0:${port}... الأمر server.bind(`0.0.0.0:${port}... لربط المنفذ وبيانات الاعتماد.

أعد كتابة الخدمة A باستخدام gRPC:

 const protoPath = `${__dirname}/../serviceB/serviceB.proto`; const proto = grpc.load(protoPath); const client = new proto.SampleDataService('localhost:8001', grpc.credentials.createInsecure()); const getDataViagRPC = () => new Promise((resolve, reject) => { client.GetSampleData({}, (err, response) => {   if (!response.err) {     resolve(response);   } else {     reject(err);   } }); }); server.route({ method: 'GET', path: '/', handler: async (request, h) => {   const allResults = await getDataViagRPC();   return h.response(allResults); }, }); 

من بين ميزات هذا الرمز ما يلي:

  • const client = new proto.SampleDataService... الأمر const client = new proto.SampleDataService... نقوم بإنشاء عميل grpc.
  • يتم إجراء مكالمة عن بعد باستخدام الأمر client.GetSampleData({}... .

الآن دعونا نختبر ما حصلنا عليه مع jMeter.


نتائج البحث من نظام الوضع العادي باستخدام gRPC باستخدام jMeter

بعد حسابات بسيطة ، يمكنك معرفة أن الحل باستخدام gRPC أسرع بنسبة 27٪ من الحل الذي يستخدم HTTP / 1.1.

تطبيق الكتلة


فيما يلي رسم تخطيطي لتطبيق مشابه للتطبيق الذي بحثنا عنه للتو ولكنه يعمل في وضع الكتلة.


بنية تطبيق وضع الكتلة

إذا قارنت هذه البنية مع ما سبق النظر فيه ، يمكن ملاحظة التغييرات التالية:

  • هناك موازن تحميل (موازن التحميل) ، والذي يستخدم NGINX .
  • الخدمة B موجودة الآن في ثلاث حالات تستمع على منافذ مختلفة.

هندسة مماثلة نموذجية للمشاريع الحقيقية.

استكشاف HTTP / 1.1 و gRPC في بيئة جديدة.

▍HTTP / 1.1


عند استخدام الخدمات الصغيرة التي تستخدم HTTP / 1.1 في بيئة متفاوتة المسافات ، لن يلزم تغيير التعليمات البرمجية الخاصة بها. تحتاج فقط إلى تكوين nginx لتنظيم موازنة حركة مرور الخدمة B. في حالتنا ، من أجل القيام بذلك ، تحتاج إلى إحضار الملف /etc/nginx/sites-available/default إلى هذا النموذج:

 upstream httpservers {  server ip_address:8001;  server ip_address:8002;  server ip_address:8003; } server {  listen 80;  location / {     proxy_pass http://httpservers;  } } 

الآن دعونا ندير ما لدينا ونلقي نظرة على نتائج اختبار النظام باستخدام jMeter.


نتائج البحث لنظام قائم على الكتلة باستخدام HTTP / 1.1 باستخدام jMeter

الوسيط في هذه الحالة هو 41 مللي ثانية.

RgRPC


ظهر دعم GRPC في nginx 1.13.10 . لذلك ، نحتاج إلى أحدث إصدار من nginx ، sudo apt-get install nginx لا يكون الأمر sudo apt-get install nginx مناسبًا.

أيضًا هنا لا نستخدم Node.js في وضع الكتلة ، لأن gRPC غير مدعوم في هذا الوضع.

لتثبيت أحدث إصدار من nginx ، استخدم تسلسل الأوامر التالي:

 sudo apt-get install -y software-properties-common sudo add-apt-repository ppa:nginx/stable sudo apt-get update sudo apt-get install nginx 

بالإضافة إلى ذلك ، سنحتاج إلى شهادات SSL. يمكن إنشاء شهادة موقعة ذاتيًا باستخدام openSSL :

 openssl req -x509 -newkey rsa:2048 -nodes -sha256 -subj '/CN=localhost' \ -keyout localhost-privatekey.pem -out localhost-certificate.pem 

لاستخدام gRPC ، تحتاج إلى تعديل الملف / etc/nginx/sites-available/default :

 upstream httpservers {  server ip_address:8001;  server ip_address:8002;  server ip_address:8003; } server {  listen 80;  location / {     proxy_pass http://httpservers;  } } 

الآن كل شيء جاهز لاختبار حل gRPC للكتلة باستخدام jMeter.


النتائج من نظام وضع الكتلة باستخدام gRPC باستخدام jMeter

في هذه الحالة ، يبلغ المتوسط ​​28 مللي ثانية ، وهو أسرع بنسبة 31٪ مقارنة بالمؤشر نفسه الذي تم الحصول عليه عند فحص حل HTTP / 1.1 متفاوت المسافات.

الملخص


تُظهر نتائج الدراسة أن تطبيقًا قائمًا على الخدمات الصغرى يستخدم gRPC أكثر إنتاجية بنسبة 30٪ تقريبًا من تطبيق مماثل يستخدم HTTP / 1.1 لتبادل البيانات بين الخدمات الصغرى. يمكن العثور على شفرة المصدر للمشاريع التي تمت مناقشتها في هذه المقالة هنا .

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

Source: https://habr.com/ru/post/ar421579/


All Articles