In letzter Zeit erfreuen sich Microservice-Architekturen einiger Beliebtheit. Die Leistung und Skalierbarkeit der darauf basierenden Lösungen kann davon abhängen, wie Microservices interagieren. Diese Interaktion kann synchron oder asynchron sein. In dem Material, dessen Übersetzung wir Ihnen heute vorstellen, werden synchrone Methoden für die Interaktion von Mikrodiensten erörtert. Wir werden uns nämlich auf die Untersuchung von zwei Technologien konzentrieren: HTTP / 1.1 und gRPC. Die erste Technologie wird durch Standard-HTTP-Aufrufe dargestellt. Die zweite basiert auf der Verwendung des leistungsstarken RPC-Frameworks von Google. Der Autor des Artikels schlägt vor, den Code zu untersuchen, der für die Implementierung der Interaktion von Microservices mithilfe von HTTP / 1.1 und gRPC erforderlich ist, Leistungsmessungen durchzuführen und eine Technologie auszuwählen, mit der der Datenaustausch zwischen Microservices optimal organisiert werden kann.

Normale Anwendung
Beginnen wir klein und erstellen ein System aus zwei Mikrodiensten, die miteinander interagieren können. Bitte beachten Sie, dass der Cluster-Modus hier nicht verwendet wird. Hier ist ein Diagramm unserer Anwendung.
Typische AnwendungsarchitekturDie Anwendung besteht aus folgenden Komponenten:
- Systemtest-Tool: jMeter .
- Dienst A: Ein Mikrodienst, der Anforderungen für Dienst B ausführt und die von ihm empfangenen Antworten zurückgibt.
- Service B (Service B): Ein Microservice, der statische JSON-Daten als Antwort auf Anforderungen nach einer Verzögerung von 10 Millisekunden sendet und für alle seine APIs verwendet wird.
- Virtuelle Maschinen (VM 1 und VM 2): Amazon EC2 t2.xlarge-Instanzen.
▍HTTP / 1.1
HTTP / 1.1 ist eine Standardtechnologie zur Organisation der Interaktion von Microservices, die bei Verwendung von HTTP-Bibliotheken wie
Axios oder
Superagenten verwendet wird .
Hier ist der Service-Code B, der die API unseres Systems implementiert:
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); }, });
Hier ist der Code für Dienst A, der über HTTP / 1.1 auf Dienst B zugreift:
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); } }, });
Durch Ausführen dieser Microservices können wir jMeter nutzen, um Leistungstests durchzuführen. Wir werden herausfinden, wie sich das System bei der Arbeit mit 50 Benutzern verhält, von denen jeder 2000 Anforderungen ausführt. Wie in der folgenden Abbildung zu sehen ist, beträgt der Median der Messergebnisse 37 ms.
Forschungsergebnisse eines Normalmodus-Systems mit HTTP / 1.1 mit jMeterRGRPC
gRPC verwendet standardmäßig die
Protokollpuffertechnologie . Daher müssen wir bei Verwendung von gRPC zusätzlich zum Code von zwei Diensten den Protodateicode schreiben, der die Schnittstelle zwischen den Systemmodulen beschreibt.
syntax = "proto3"; service SampleDataService { rpc GetSampleData (Empty) returns (SampleData) {} } message SampleData { int32 id = 1; string name = 2; bool enjoys_coding = 3; } message Empty {}
Da wir jetzt planen, gRPC zu verwenden, müssen wir den Servicecode B neu schreiben:
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');
Beachten Sie einige Funktionen dieses Codes:
- Der Befehl
const server = new grpc.Server();
Wir erstellen einen Grpc-Server. server.addService(proto...
Befehl server.addService(proto...
fügen wir den Dienst dem Server hinzu.- Der Befehl
server.bind(`0.0.0.0:${port}...
wird verwendet, um den Port und die Anmeldeinformationen zu binden.
Schreiben Sie nun Service A mit gRPC neu:
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); }, });
Zu den Funktionen dieses Codes gehören:
const client = new proto.SampleDataService...
Befehl const client = new proto.SampleDataService...
erstellen wir einen grpc-Client.- Ein Remote-Aufruf erfolgt mit dem
client.GetSampleData({}...
.
Testen wir nun, was wir mit jMeter bekommen haben.
Forschungsergebnisse eines Normalmodus-Systems mit gRPC mit jMeterNach einfachen Berechnungen können Sie feststellen, dass eine Lösung mit gRPC 27% schneller ist als eine Lösung mit HTTP / 1.1.
Cluster-Anwendung
Hier ist ein Diagramm einer Anwendung, die der gerade untersuchten ähnelt, aber im Cluster-Modus arbeitet.
Anwendungsarchitektur im Cluster-ModusWenn Sie diese Architektur mit der zuvor betrachteten vergleichen, können die folgenden Änderungen festgestellt werden:
- Es gibt einen Load Balancer (Load Balancer), der NGINX verwendet .
- Service B ist jetzt in drei Instanzen vorhanden, die verschiedene Ports überwachen.
Eine ähnliche Architektur ist typisch für reale Projekte.
Erkundung von HTTP / 1.1 und gRPC in einer neuen Umgebung.
▍HTTP / 1.1
Bei Verwendung von Microservices, die HTTP / 1.1 in einer Clusterumgebung verwenden, muss deren Code nicht geändert werden. Sie müssen nur nginx konfigurieren, um den Ausgleich des Service-B-Verkehrs zu organisieren. In unserem Fall müssen Sie dazu die Datei
/etc/nginx/sites-available/default
in dieses Formular bringen:
upstream httpservers { server ip_address:8001; server ip_address:8002; server ip_address:8003; } server { listen 80; location / { proxy_pass http://httpservers; } }
Lassen Sie uns nun das, was wir haben, ausführen und die Ergebnisse des Testens des Systems mit jMeter betrachten.
Forschungsergebnisse für ein clusterbasiertes System unter Verwendung von HTTP / 1.1 unter Verwendung von jMeterDer Median beträgt in diesem Fall 41 ms.
RGRPC
Die GRPC-Unterstützung wurde in
Nginx 1.13.10 veröffentlicht . Daher benötigen wir die neueste Version von nginx, für deren Installation der übliche Befehl
sudo apt-get install nginx
nicht geeignet ist.
Auch hier verwenden wir Node.js nicht im
Cluster- Modus, da gRPC in diesem Modus
nicht unterstützt wird .
Verwenden Sie die folgende Befehlsfolge, um die neueste Version von nginx zu installieren:
sudo apt-get install -y software-properties-common sudo add-apt-repository ppa:nginx/stable sudo apt-get update sudo apt-get install nginx
Außerdem benötigen wir SSL-Zertifikate. Ein selbstsigniertes Zertifikat kann mit
openSSL erstellt werden:
openssl req -x509 -newkey rsa:2048 -nodes -sha256 -subj '/CN=localhost' \ -keyout localhost-privatekey.pem -out localhost-certificate.pem
Um gRPC verwenden zu können, müssen Sie die Datei /
etc/nginx/sites-available/default
bearbeiten:
upstream httpservers { server ip_address:8001; server ip_address:8002; server ip_address:8003; } server { listen 80; location / { proxy_pass http://httpservers; } }
Jetzt ist alles bereit, um die Cluster-gRPC-Lösung mit jMeter zu testen.
Ergebnisse eines Cluster-Modus-Systems mit gRPC mit jMeterIn diesem Fall beträgt der Median 28 ms, was 31% schneller ist als derselbe Indikator, der bei der Untersuchung einer Cluster-HTTP / 1.1-Lösung erhalten wurde.
Zusammenfassung
Die Ergebnisse der Studie zeigen, dass eine auf Mikroservices basierende Anwendung, die gRPC verwendet, etwa 30% produktiver ist als eine ähnliche Anwendung, die HTTP / 1.1 zum Datenaustausch zwischen Microservices verwendet. Den Quellcode für die in diesem Artikel behandelten Projekte finden Sie
hier .
Liebe Leser! Wenn Sie Microservices entwickeln, sprechen Sie bitte darüber, wie Sie den Datenaustausch zwischen ihnen organisieren.
