Organisation d'une interaction efficace des microservices

Récemment, les architectures de microservices ont connu une certaine popularité. Les performances et l'évolutivité des solutions basées sur celles-ci peuvent dépendre de l'interaction des microservices. Cette interaction peut être synchrone ou asynchrone. Le matériel, dont nous présentons aujourd'hui la traduction, traite des méthodes synchrones d'interaction des microservices. À savoir, nous nous concentrerons sur l'étude de deux technologies: HTTP / 1.1 et gRPC. La première technologie est représentée par des appels HTTP standard. Le second est basé sur l'utilisation du framework RPC hautes performances de Google. L'auteur de l'article suggère de regarder le code nécessaire pour implémenter l'interaction des microservices à l'aide de HTTP / 1.1 et gRPC, de prendre des mesures de performances et de choisir une technologie qui permet d'organiser au mieux l'échange de données entre les microservices.



Application normale


Commençons petit et créons un système de deux microservices qui peuvent interagir l'un avec l'autre. Veuillez noter que le mode cluster n'est pas utilisé ici. Voici un schéma de notre application.


Architecture d'application typique

L'application se compose des composants suivants:

  • Outil de test du système: jMeter .
  • Service A: microservice qui exécute les demandes de service B et renvoie les réponses reçues de celui-ci.
  • Service B (Service B): un microservice qui envoie des données JSON statiques en réponse aux demandes après un délai de 10 millisecondes utilisé pour toutes ses API.
  • Machines virtuelles (VM 1 et VM 2): instances Amazon EC2 t2.xlarge.

▍HTTP / 1.1


HTTP / 1.1 est une technologie standard pour organiser l'interaction des microservices, qui est utilisée lors de l'utilisation de bibliothèques HTTP comme axios ou superagent .

Voici le code de service B qui implémente l'API de notre système:

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);  }, }); 

Voici le code du service A qui accède au service B via 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); } }, }); 

En exécutant ces microservices, nous pouvons profiter de jMeter pour exécuter des tests de performances. Nous découvrirons comment le système se comporte lorsqu'il travaille avec 50 utilisateurs, chacun effectuant 2000 requêtes. Comme le montre la figure suivante, la médiane des résultats de mesure est de 37 ms.


Résultats de recherche d'un système en mode normal utilisant HTTP / 1.1 à l'aide de jMeter

▍gRPC


gRPC utilise la technologie Protocol Buffers par défaut. Par conséquent, en utilisant gRPC, en plus du code de deux services, nous devrons écrire le code de proto-fichier, qui décrit l'interface entre les modules du système.

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

Maintenant, puisque nous prévoyons maintenant d'utiliser gRPC, nous devons réécrire le code de service 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'); 

Faites attention à certaines fonctionnalités de ce code:

  • La commande const server = new grpc.Server(); nous créons un serveur grpc.
  • server.addService(proto... commande server.addService(proto... nous ajoutons le service au serveur.
  • La commande server.bind(`0.0.0.0:${port}... est utilisée pour lier le port et les informations d'identification.

Maintenant, réécrivez le service A à l'aide de 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); }, }); 

Parmi les fonctionnalités de ce code figurent les suivantes:

  • const client = new proto.SampleDataService... commande const client = new proto.SampleDataService... nous créons un client grpc.
  • Un appel distant est effectué à l'aide de la commande client.GetSampleData({}...

Maintenant, testons ce que nous avons obtenu avec jMeter.


Résultats de recherche d'un système en mode normal utilisant gRPC utilisant jMeter

Après de simples calculs, vous pouvez découvrir qu'une solution utilisant gRPC est 27% plus rapide qu'une solution utilisant HTTP / 1.1.

Application de cluster


Voici un schéma d'une application similaire à celle que nous venons de rechercher mais fonctionnant en mode cluster.


Architecture d'application en mode cluster

Si vous comparez cette architecture avec la précédente, les changements suivants peuvent être notés:

  • Il existe un équilibreur de charge (Load Balancer), qui utilise NGINX .
  • Le service B est maintenant présent dans trois instances qui écoutent sur différents ports.

Une architecture similaire est typique des projets réels.

Explorer HTTP / 1.1 et gRPC dans un nouvel environnement.

▍HTTP / 1.1


Lorsque vous utilisez des microservices qui utilisent HTTP / 1.1 dans un environnement en cluster, leur code ne devra pas être modifié. Il vous suffit de configurer nginx pour organiser l'équilibrage du trafic du service B. Dans notre cas, pour ce faire, vous devez apporter le fichier /etc/nginx/sites-available/default sous cette forme:

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

Maintenant, exécutons ce que nous avons et examinons les résultats du test du système à l'aide de jMeter.


Résultats de recherche pour un système basé sur un cluster utilisant HTTP / 1.1 à l'aide de jMeter

La médiane dans ce cas est de 41 ms.

▍gRPC


Le support GRPC est apparu dans nginx 1.13.10 . Par conséquent, nous avons besoin de la dernière version de nginx, pour l'installation de laquelle la commande habituelle sudo apt-get install nginx ne convient pas.

Ici aussi, nous n'utilisons pas Node.js en mode cluster , car gRPC n'est pas pris en charge dans ce mode.

Pour installer la dernière version de nginx, utilisez la séquence de commandes suivante:

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

De plus, nous aurons besoin de certificats SSL. Un certificat auto-signé peut être créé en utilisant openSSL :

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

Pour utiliser gRPC, vous devez éditer le fichier / 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;  } } 

Maintenant, tout est prêt pour tester la solution gRPC de cluster à l'aide de jMeter.


Résultats d'un système en mode cluster utilisant gRPC à l'aide de jMeter

Dans ce cas, la médiane est de 28 ms, ce qui est 31% plus rapide par rapport au même indicateur obtenu lors de l'examen d'une solution HTTP / 1.1 en cluster.

Résumé


Les résultats de l'étude montrent qu'une application basée sur un microservice qui utilise gRPC est environ 30% plus productive qu'une application similaire qui utilise HTTP / 1.1 pour échanger des données entre microservices. Le code source des projets discutés dans cet article peut être trouvé ici .

Chers lecteurs! Si vous développez des microservices, veuillez expliquer comment vous organisez l'échange de données entre eux.

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


All Articles