Приложение, работающее в обычном режиме
Начнём с малого и создадим систему из двух микросервисов, которые могут взаимодействовать друг с другом. Обратите внимание на то, что здесь не используется кластерный режим. Вот схема нашего приложения.

Архитектура приложения, работающего в обычном режиме
Приложение состоит из следующих компонентов:
- Средство для тестирования системы (Load testing tool): jMeter.
- Сервис А (Service A): микросервис, который выполняет запросы к сервису B и возвращает полученные от него ответы.
- Сервис B (Service B): микросервис, отправляющий в ответ на запросы статические JSON-данные после 10-миллисекундной задержки, используемой для всех его API.
- Виртуальные машины (VM 1 и VM 2): экземпляры Amazon EC2 t2.xlarge.
▍HTTP/1.1
HTTP/1.1 — это стандартная технология организации взаимодействия микросервисов, которая применяется при использовании любых HTTP-библиотек вроде axios или superagent.
Вот код сервиса B, реализующий API нашей системы:
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); }, });
Вот код сервиса А, который обращается к сервису B, используя HTTP/1.1:
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); }, });
Запустив эти микросервисы, мы можем воспользоваться возможностями jMeter для выполнения тестов производительности. Выясним, как система ведёт себя при работе с ней 50 пользователей, каждый из которых выполняет по 2000 запросов. Как можно видеть на следующем рисунке, медиана результатов измерения равна 37 мс.

Результаты исследования системы, работающей в обычном режиме и использующей HTTP/1.1, с помощью jMeter
▍gRPC
gRPC использует по умолчанию технологию Protocol Buffers. Поэтому, применяя gRPC, в дополнение к коду двух сервисов, нам понадобится написать и код proto-файла, который описывает интерфейс взаимодействия модулей системы.
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();мы создаём grpc-сервер. - Командой
server.addService(proto...мы добавляем сервис к серверу. - Команда
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...мы создаём grpc-клиент. - Удалённый вызов выполняется с помощью команды
client.GetSampleData({}....
Теперь протестируем то, что у нас получилось, с помощью jMeter.

Результаты исследования системы, работающей в обычном режиме и использующей gRPC, с помощью jMeter
Проведя несложные расчёты, можно выяснить, что решение, использующее gRPC, оказывается на 27% быстрее решения, использующего HTTP/1.1.
Приложение, работающее в кластерном режиме
Вот схема приложения, аналогичного тому, которое мы только что исследовали, но работающего в кластерном режиме.

Архитектура приложения, работающего в кластерном режиме
Если сравнить эту архитектуру с рассмотренной ранее, можно отметить следующие изменения:
- Здесь имеется балансировщик нагрузки (Load Balancer), в роли которого используется 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 мс.
▍gRPC
Поддержка gRPC появилась в nginx 1.13.10. Поэтому нам понадобится самая свежая версия 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 мс, а это, в сравнении с аналогичным показателем, полученным при исследовании кластерного HTTP/1.1-решения, на 31% быстрее.
Итоги
Результаты исследования показывают, что приложение, основанное на микросервисах, и использующее gRPC, оказывается примерно на 30% производительнее аналогичного приложения, в котором для обмена данными между микросервисами используется HTTP/1.1. Исходный код проектов, рассмотренных в этом материале, можно найти здесь.
Уважаемые читатели! Если вы занимаетесь разработкой микросервисов, просим рассказать о том, как вы организуете обмен данными между ними.
ссылка на оригинал статьи https://habr.com/post/421579/


Добавить комментарий