Привет! Меня зовут Игорь Закутинский, я Head of Engineering GetProspect — украинского инструмента лидогенерации, позволяющего находить тысячи имейлов в LinkedIn за несколько кликов.
В этой статье я хочу поделиться собственным опытом миграции одного из сервисов проекта с HTTP API на GRPC, что позволило компании оптимизировать ресурсы и значительно увеличить ее производительность.
Краткий обзор платформы для лидогенерации GetProspect
Главная задача нашего сервиса — поиск электронного адреса определенного профиля в LinkedIn. Это происходит благодаря алгоритму, анализирующему данные, которые указывает пользователь в соцсети. После того, как все имейлы найдены, благодаря GetProspect можно легко управлять своими контактами в нашей CRM-системе. Для того, чтобы база электронных почт была максимально полной, пользователь дополнительно может загружать в нее данные из следующих источников:
- Google Chrome extension;
- CSV-файл;
- через API;
- интеграции со сторонними сервисами (например, Zapier);
- Google-таблицы.
Поиск имейл-адреса, энричмент данных, экспорты и импорты — ресурсозатратные задачи. Поэтому эти процессы выполняются через очереди с определенными лимитами для равномерного распределения ресурсов сервиса между всеми пользователями.
Почему мы используем микросервисы?
Нагрузка динамическая и труднопрогнозируемая, поэтому основные требования к архитектуре сервиса — доступность и масштабируемость.
GetProspect реализован по классической микросервисной архитектуре, где каждый микросервис отвечает следующим критериям:
- фокус на конкретные бизнес-задачи;
- независимость от языка программирования и фреймворков;
- простота тестирования;
- развертывание и деплоймент конкретного сервиса не зависит от других сервисов.
Микросервисная коммуникация
Большинство сервисов должны взаимодействовать как с внутренними, так и внешними ресурсами. Поэтому одна из главных составляющих, влияющей на производительность системы — микросервисная коммуникация.
Наши микросервисы используют HTTP RESTfull API в качестве базового протокола обмена данными.
REST (Representational State Transfer) — это архитектурный стиль взаимодействия распределенных компонентов в сети.
Основные преимущества REST API и почему мы его используем:
- интуитивно понятный;
- прост в масштабировании;
- прост в поддержке и отладке;
- структура API не зависит от технологии и языка программирования.
HTTP REST API удовлетворяет основные потребности обмена данными между микросервисами, но из-за конструктивных особенностей не может использоваться во всех случаях.
Одна из основных задач нашего инструмента лидогенерации — поиск имейла пользователя. Поиск электронной почты основан на SMTP-диалоге с email-сервером и может занять от нескольких секунд до 10 минут.
SMTP (Simple Mail Transfer Pro tocol) — это коммуникационный протокол для передачи электронной почты.
Процесс поиска и валидации email-адресов реализован на отдельном микросервисе — Mail verification service, а логика сохранения и управления данными — на API service.
На первом этапе реализация выглядела следующим образом:
Вышеприведенная схема достаточно упрощена, в ней не показаны многие элементы, такие как очереди, балансировка, кэш и т.д. Но его вполне достаточно для общего понимания процесса.
Как видно из схемы, API service инициирует поиск имейл-адреса на Mail verification service с помощью HTTP-запроса, и через некоторое время, когда будет завершен процесс поиска, Mail verification service возвращает результат также через HTTP-запрос на API Service.
То есть для того, чтобы найти email-адрес, выполнялось как минимум два HTTP-запроса (на самом деле их было немного больше 😉), что порождало несколько проблем:
- Во-первых, влияние на скорость и производительность. Каждый HTTP-запрос инициирует новое клиент-сервер-соединение, создающее большую нагрузку.
- Во-вторых, это то, что запросы не имели общего контекста, поскольку в REST-архитектуре отсутствует сохранение состояния запроса (stateless), то есть сервер выполняет каждый запрос клиента независимо от предыдущих.
В качестве оптимизации мы рассмотрели использование GRPC для коммуникации этих двух микросервисов, поскольку в нем архитектурно устранены недостатки, о которых я писал выше.
Что такое GRPC? Почему мы выбрали его для микросервисной коммуникации?
GRPC — это современный протокол RPC, реализуемый поверх HTTP/2.
HTTP/2 — это протокол уровня 7 (уровень приложений), работающий поверх TCP (уровень 4 — транспортный уровень), который работает поверх протокола IP (уровень 3 — сетевой уровень).
У GRPC есть множество преимуществ перед традиционным HTTP/REST/JSON, например:
- двоичный протокол (HTTP/2);
- мультиплексирование многих запросов на одном соединении (HTTP/2);
- сжатие заголовка (HTTP/2);
- строго типизированный сервис и сообщение (Protobuf);
- сериализация/десериализация данных.
Сообщения GRPC сериализируются с помощью Protobuf — эффективного двоичного формата сообщений. Protobuf быстро выполняет сериализацию на сервере и клиенте.
С помощью Protobuf–сериализации нам удалось значительно снизить объем полезных данных, а значит увеличить пропускную способность между API-сервисом и сервисом, который процессит поиск email-адресов.
Также значительно упростилась логика поиска. На стороне API service процесс поиска имейла выглядит как вызов асинхронной функции (удаленной процедуры в случае gRPC 😉).
Тест производительности: GRPC vs HTTP Restful API
Я решил провести тест, для которого использовался два AWS EC2 c5.2xLarge instance, на которых были запущены Node.js application — клиент и сервер.
В этом тесте клиент делает простой запрос на сервер или вызывает процедуру, а сервер просто возвращает значение без какой-либо сложной логики, поскольку цель — определить производительность протокола. В тесте определяется количество возможных запросов за единицу времени (1 секунду, а также время на выполнение 100к запросов или вызовов процедур).
gRPC server, который использовался для теста:
gRPC client, который использовался для теста:
Для теста HTTP Rest API реализована аналогичная логика на Fastify и Axios.
Результаты теста:
HTTP Post request | gRPC procedure call | |
100k async requests, sec | 32.342 | 4.370 |
Requests per second | 2147 | 26102 |
CPU usage – ms/req | 491 ms / per request | 211 ms / per request |
Как видно из графика, пропускная способность GRPC более чем в восемь раз выше HTTP API, что значительно повышает производительность на больших объемах запросов. Также следует отметить то, что CPU-время на 1 gRPC-процедуру более чем в два раза меньше, чем на аналогичный HTTP-запрос.
Балансировка нагрузки
Поскольку общая нагрузка на сервис постоянно меняется, возникает необходимость масштабирования каждого отдельного микросервиса, и, соответственно, балансировка нагрузки между ними. Балансировка GRPC-сервисов несколько сложнее балансировки сервисов с HTTP-сервером.
Существует два основных подхода к GRPC-балансировке:
- на стороне клиента;
- балансировка через proxy-сервер.
Преимущество клиентской балансировки — прямое соединение клиент-сервер, обеспечивающее минимальное время обмена сообщениями. Тем не менее при таком подходе есть много минусов, из-за которых мы отказались от такой реализации, а именно:
- сложность реализации клиента;
- клиент отслеживает нагрузку и доступность сервера;
- необходимость реализации и поддержания алгоритма балансировки.
Эти проблемы значительно усложняются при росте числа клиентов.
В нашем случае балансировка реализована через Haproxy, что значительно упрощает реализацию и полностью удовлетворяет нашим требованиям по производительности.
Безопасность в GRPC-микросервисах
GRPC поддерживает SSL/TLS encryption, что позволяет шифровать данные, которыми обменивается клиент и сервер. Также GRPC предоставляет простой Auth API, позволяющий предоставить авторизационные данные при создании канала или вызове функции.
Пример авторизации через SSL/TLS-сертификаты:
const cert = fs.readFileSync('path/to/cert'); const ssl = grpc.credentials.createSsl(cert); const stub = new service.Verification('grpc.service.com', ssl);
Слабые стороны GRPC
Браузерная поддержка
К сожалению, сегодня не существует полноценной браузерной поддержки GRPC, поэтому его не стоит использовать для служб, вызываемых непосредственно из браузерных приложений.
Недоступно для чтения человеком
Запросы HTTP Rest API отправляются в текстовом виде (JSON), что позволяет разработчикам их легко читать и редактировать. gRPC использует двоичный протокол Protobuf, что значительно эффективнее, но не читаемо человеком, что затрудняет и замедляет процесс отладки и разработки.
Выводы
На сегодня GRPC — перспективное решение для микросервисов, где необходима высокая пропускная способность. Его использование позволило нам оптимизировать bottleneck нашей системы, значительно сократить расходы на трафик и увеличить производительность.
Тем не менее из-за сложности в реализации, а также отсутствия полноценной браузерной поддержки мы не можем рассматривать GRPC как полноценного конкурента HTTP REST API.
Делитесь вашими мыслями, мигрировали ли вы с HTTP API на GRPC, и каковы результаты?
Этот материал – не редакционный, это – личное мнение его автора. Редакция может не разделять это мнение.
Сообщить об опечатке
Текст, который будет отправлен нашим редакторам: