Как мы используем микросервисы и GRPC
Предисловие
Наш основной бизнес — хостинг. Чтобы пользователям было удобно работать с хостингом, нужно предоставить им панель управления.
Так сложилось, что мы не взяли готовую панель управления для хостинга, а выбрали более сложный путь — решили сделать панель управления сами. Постепенно в нее добавлялись биллинг, хелпдеск для технической поддержки, админская панель и много что еще.
Спустя больше 16 лет в проекте накопились десятки тысяч строк кода на Python 2.7. Такие проекты называют монолитом. Вот и у нас получился большой и сложно управляемый монолит
Монолит в фильме “Космическая одиссея 2001 года” (1968)
У нас появилось несколько проблем:
- Крайне медленная разработка новых фич. Есть множество зависимостей, и новый код может легко сломать старый код.
- Сложный старт для новых разработчиков. В среднем нужно пол-года разбираться со всем проектом, чтобы начать делать что-то полезное.
- Скудный набор тестов, нет CI/CD
- Всё написано на python 2.7, а хотелось 3.8 и/или golang
Чтобы избавиться от этих проблем, с 2017 года мы решили переходить на использование микросервисов.
Хорошо, переходим на микросервисы. Что дальше?
Теперь надо определить как связать между собой микросервисы. Для связи с первым микросервисом мы использовали REST API. Мы взяли Swagger, чтобы стандартизировать и документировать полученное API.
Вот какие минусы этой связки мы обнаружили:
1. В REST нужно придумать все параметры URL микросервиса. Нужно писать стандарты. Мы придумали сначала одну версию стандарта. Оказалось, что продумали не все. Пришлось разработать еще одну версию. А по мере увеличения числа микросервисов наверняка потребовалось бы ее дорабатывать.
2. Чтобы работать с REST API пробовали библиотеку beego для языка Go. Beego выдавал swagger файл, но библиотеки в Python его не понимали.
3. Кроме самого микросервиса нужно было писать код, чтобы наш монолит мог работать с микросервисом. И скорость разработки замедлилась ещё больше.
С REST оказалось не очень удобно. А чем лучше будет GRPC?
- Нет проблем REST API (привязка к http-методам, статусам и url).
- Современный HTTP 2 из коробки.
- Мультиплексирование. Данные загружаются в рамках одного соединения, а не отдельное соединение на каждый ресурс как в HTTP 1.1.
- Двунаправленная связь. Запросы не только от клиента к серверу, но и наоборот.
- Поддержка C++, C#, Dart, Java, Python, Golang, Node.js, Ruby, PHP, Objective-C.
- Авторизация в виде плагинов.
Описание структур в GRPC идет через Protobuf. Что такое Protobuf? Это альтернатива XML, только лучше:
- Более простой синтаксис
- От 3 до 10 раз компактнее
- До 100 раз быстрее обработка файла
Вот как может выглядеть структура Protobuf для описания автомобиля:
message Owner {
string name = 1;
string license = 2;
}
enum BodyType {
sedan = 1;
hatchback = 2;
}
message Car {
string model = 1;
int32 year = 2;
BodyType type = 3;
repeated Owner owner = 4;
}
Подробнее о Protobuf на английском языке.
А как отлаживать вызовы в GRPC? Можно ли отправлять curl запросы как в REST?
GRPC — бинарный протокол, поэтому сразу работать с ним как с REST через curl или wget не получится. Но есть расширение, которое называется Rest GRPC Gateway. Он работает как прокси сервер:
Какой вебсервер умеет работать с GRPC?
В nginx начиная с версии 1.13.10 есть поддержка GRPC.
Пример конфига nginx, который проксирует GRPC:
location /helloworld.Greeter {
grpc_pass grpc://192.168.20.11:50051;
}
location /helloworld.Dispatcher {
grpc_pass grpc://192.168.20.11:50052;
}
Есть поддержка upstream, значит можно запустить несколько экземпляров микросервиса на разных IP. А nginx станет автоматически балансировать нагрузку между ними.
Если один из экземпляров микросервиса начнет отвечать ошибками, то nginx временно перестанет отправлять на него запросы. Очень удобно.
Собираем все кусочки вместе. GRPC в production
Вот как у нас это работает в “боевом” окружении:
Gateway API делает централизованную авторизацию. Еще он знает в каком микросервисе находится какая бизнес логика.
AGENT - на каждом железном сервере у нас установлена небольшая программа-клиент, которая от микросервисов принимает команды и выполняет их. Раньше к похожим агентам на серверах мы обращались по SSH. Теперь стали обращаться тоже по GRPC.
Какой профит мы получили от GRPC в production?
Приведем пример для одного из сервисов — почты.
У нас десятки тысяч почтовых ящиков и много железных почтовых серверов. Раз в час мы проходимся биллингом по всем серверам и подсчитываем какой ящик сколько занимает места, кто превысил квоту и тд.
Раньше это делал скрипт на Python последовательно обходя все сервера по SSH. И занимало это 15 минут.
Мы переписали скрипт на Golang. Используя возможности Golang, скрипт начал ходить на сервера параллельно. А еще мы изменили транспорт на GRPC. И теперь подсчет занимает 15 секунд. Если убрать подробное логирование в скрипте, то все выполняется вообще за 3 секунды.
Вот какие выводы мы сделали после работы с GRPC:
- Очень быстрый
- Сокращает время разработки
- GRPC отлично подходит для организации взаимодействия микросервисов
- Более удобный, чем REST
- Отличная обратная совместимость