Recentemente, as arquiteturas de microsserviço desfrutaram de alguma popularidade. O desempenho e a escalabilidade das soluções baseadas nelas podem depender de como os microsserviços interagem. Essa interação pode ser síncrona ou assíncrona. O material, cuja tradução apresentamos hoje à sua atenção, discute métodos síncronos para a interação de microsserviços. Nomeadamente, nos concentraremos no estudo de duas tecnologias: HTTP / 1.1 e gRPC. A primeira tecnologia é representada por chamadas HTTP padrão. O segundo é baseado no uso da estrutura RPC de alto desempenho do Google. O autor do artigo sugere analisar o código necessário para implementar a interação de microsserviços usando HTTP / 1.1 e gRPC, fazer medições de desempenho e escolher uma tecnologia que permita organizar da melhor maneira a troca de dados entre microsserviços.

Aplicação normal
Vamos começar pequenos e criar um sistema de dois microsserviços que podem interagir entre si. Observe que o modo de cluster não é usado aqui. Aqui está um diagrama de nossa aplicação.
Arquitetura típica de aplicativosO aplicativo consiste nos seguintes componentes:
- Ferramenta de Teste do Sistema: jMeter .
- Serviço A: Um microsserviço que executa solicitações para o serviço B e retorna as respostas recebidas dele.
- Serviço B (Serviço B): um microsserviço que envia dados JSON estáticos em resposta a solicitações após um atraso de 10 milissegundos usado para todas as suas APIs.
- Máquinas virtuais (VM 1 e VM 2): instâncias do Amazon EC2 t2.xlarge.
▍HTTP / 1.1
O HTTP / 1.1 é uma tecnologia padrão para organizar a interação de microsserviços, usada ao usar bibliotecas HTTP como
axios ou
superagent .
Aqui está o código de serviço B que implementa a API do nosso sistema:
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); }, });
Aqui está o código do serviço A que acessa o serviço B usando 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); } }, });
Ao executar esses microsserviços, podemos tirar proveito do jMeter para executar testes de desempenho. Vamos descobrir como o sistema se comporta ao trabalhar com 50 usuários, cada um dos quais realiza 2000 solicitações. Como pode ser visto na figura a seguir, a mediana dos resultados da medição é de 37 ms.
Resultados de pesquisa de um sistema de modo normal usando HTTP / 1.1 usando jMeter▍gRPC
O gRPC usa a tecnologia
Buffers de Protocolo por padrão. Portanto, usando o gRPC, além do código de dois serviços, precisaremos escrever o código do arquivo proto, que descreve a interface entre os módulos do sistema.
syntax = "proto3"; service SampleDataService { rpc GetSampleData (Empty) returns (SampleData) {} } message SampleData { int32 id = 1; string name = 2; bool enjoys_coding = 3; } message Empty {}
Agora, como agora planejamos usar o gRPC, precisamos reescrever o código de serviço 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');
Preste atenção a alguns recursos deste código:
- O comando
const server = new grpc.Server();
criamos um servidor grpc. server.addService(proto...
comando server.addService(proto...
adicionamos o serviço ao servidor.- O comando
server.bind(`0.0.0.0:${port}...
é usado para ligar a porta e as credenciais.
Agora reescreva o serviço A usando 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); }, });
Entre os recursos desse código estão os seguintes:
const client = new proto.SampleDataService...
comando const client = new proto.SampleDataService...
criamos um cliente grpc.- Uma chamada remota é feita usando o
client.GetSampleData({}...
.
Agora vamos testar o que obtivemos com o jMeter.
Resultados de pesquisa de um sistema de modo normal usando gRPC usando jMeterApós cálculos simples, você pode descobrir que uma solução usando gRPC é 27% mais rápida que uma solução usando HTTP / 1.1.
Aplicativo de cluster
Aqui está um diagrama de um aplicativo semelhante ao que acabamos de pesquisar, mas trabalhando no modo de cluster.
Arquitetura de aplicativos no modo de clusterSe você comparar essa arquitetura com a considerada anteriormente, as seguintes alterações poderão ser observadas:
- Há um balanceador de carga (Load Balancer), que usa o NGINX .
- O serviço B agora está presente em três instâncias que escutam em portas diferentes.
Uma arquitetura semelhante é típica para projetos reais.
Explorando HTTP / 1.1 e gRPC em um novo ambiente.
▍HTTP / 1.1
Ao usar microsserviços que usam HTTP / 1.1 em um ambiente em cluster, seu código não precisará ser alterado. Você só precisa configurar o nginx para organizar o balanceamento do tráfego do serviço B. No nosso caso, para fazer isso, você precisa trazer o arquivo
/etc/nginx/sites-available/default
para este formulário:
upstream httpservers { server ip_address:8001; server ip_address:8002; server ip_address:8003; } server { listen 80; location / { proxy_pass http://httpservers; } }
Agora vamos executar o que temos e ver os resultados do teste do sistema usando o jMeter.
Resultados da pesquisa para um sistema baseado em cluster usando HTTP / 1.1 usando jMeterA mediana neste caso é 41 ms.
▍gRPC
O suporte ao GRPC apareceu no
nginx 1.13.10 . Portanto, precisamos da versão mais recente do nginx, para a instalação em que o comando
sudo apt-get install nginx
usual não é adequado.
Também aqui não usamos Node.js no modo de
cluster , pois o gRPC
não é
suportado nesse modo.
Para instalar a versão mais recente do nginx, use a seguinte sequência de comandos:
sudo apt-get install -y software-properties-common sudo add-apt-repository ppa:nginx/stable sudo apt-get update sudo apt-get install nginx
Além disso, precisaremos de certificados SSL. Um certificado autoassinado pode ser criado usando o
openSSL :
openssl req -x509 -newkey rsa:2048 -nodes -sha256 -subj '/CN=localhost' \ -keyout localhost-privatekey.pem -out localhost-certificate.pem
Para usar o gRPC, você precisa editar o arquivo /
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; } }
Agora tudo está pronto para testar a solução de gRPC de cluster usando o jMeter.
Resultados de um sistema em modo de cluster usando gRPC usando jMeterNesse caso, a mediana é de 28 ms, 31% mais rápida em comparação com o mesmo indicador obtido ao examinar uma solução HTTP / 1.1 em cluster.
Sumário
Os resultados do estudo mostram que um aplicativo baseado em microsserviço que usa gRPC é cerca de 30% mais produtivo do que um aplicativo semelhante que usa HTTP / 1.1 para trocar dados entre microsserviços. O código fonte dos projetos discutidos neste artigo pode ser encontrado
aqui .
Caros leitores! Se você estiver desenvolvendo microsserviços, fale sobre como você organiza a troca de dados entre eles.
