Привет, Хабр. Для будущих студентов курса "Highload Architect" подготовили перевод материала.
Также приглашаем зарегистрироваться на открытый вебинар на тему «Репликация как паттерн горизонтального масштабирования хранилищ» с Владиславом Родиным (руководитель группы и специалист по Java Enterprise разработке). На занятии участники разберут репликацию — одну из техник масштабирования баз данных.
У меня есть несколько микросервисов, которые общаются друг с другом с помощью JSON через REST, и мне кажется, что скоро они достигнут предела производительности VPS, и мне нужно будет его апгрейдить, чтобы не было просадки по производительности.
И я решил оценить, какой выигрыш в производительности я смогу получить от gRPC (если вообще смогу) и, что более важно, сколько усилий мне придется приложить, чтобы полностью перевести все мои микросервисы на gRPC.
Что такое gRPC?
Для тех, кто еще не слышал о gRPC, gRPC — это не зависящий от языка фреймворк для удаленного вызова процедур (RPC, remote procedure calls), разработанный Google, которая серьезно вкладывается в производительность и масштабирование. Он появился довольно давно, но многие (а, может быть, только я) отложили его на второй план из-за затрат на написание IDL и дополнительного кода стабов (stub), который тоже необходимо поддерживать. В то же время REST очень легко реализуется с помощью ASP.NET Core WebAPI.
Аналогично использованию JSON для REST, для gRPC используется Protocol Buffers — не зависящий от языка формат для сериализации структурированных данных. Этим gRPC отличается от REST. Поддержка Protocol Buffers есть для всех основных языков благодаря компилятору protoc, который генерирует необходимый исходный код классов из определений в proto-файле. Еще важный момент состоит в том, что для связи gRPC использует HTTP/2, а это приносит дополнительные преимущества, такие как сжатие HTTP-заголовков и мультиплексирование запросов.
Ограничения бенчмарка
Было несколько ограничений, которые я хотел реализовать при бенчмаркинге:
Тестировать только чистую пропускную способность связи и не включать в процесс бизнес-логику. Я чувствовал, что это отвлечет от главного.
Не использовать базу данных. Вместо этого имитировать данные с помощью статических полей. Таким образом устраняются проблемы с кешированием и случайным искажением времени выполнения запроса.
Отключить логгирование, чтобы оно не влияло на тесты. Оно может сильно исказить результаты.
Структура проекта
ModelLibrary содержит REST и gRPC-модели. Чтобы сделать тест более обобщенным, я выбрал набор данных, который включает в себя строки, целые и вещественные числа, дату и время. Я взял данные NASA о метеоритах, состоящие из 1000 единиц данных, отсюда.
Также для gRPC-сервиса были созданы следующие определения Protocol Buffers для сообщений и сервисов.
Я также тестирую производительность потоковой передачи (stream) gRPC с помощью сервиса GetLargePayload
.
RestAPI содержит WebAPI, который предоставляет три метода, направленные на следующие сценарии: получение небольшой полезной нагрузки, получение большой полезной нагрузки и отправка большой полезной нагрузки.
Как я уже упоминал выше в разделе об ограничениях, я убрал лишнее логирование, чтобы оно не влияло на результаты бенчмарка.
GrpcAPI содержит реализацию сервиса и некоторый код инициализации для запуска gRPC-сервера. Мне также было интересно сравнить потоковую передачу, поэтому я реализовал дополнительный метод
GetLargePayload
, который итерируется по данным и за один раз отправляет одну единицу данных.
RESTvsGRPC содержит бенчмарк, который вызывает все эти методы двумя партиями по 100 и 200 итераций каждая, чтобы уменьшить погрешность измерения от малого времени выполнения одиночного запроса. Таким образом, приведенное ниже время выполнения тестов на самом деле больше в 100 и 200 раз.
Результаты
Как и ожидалось, gRPC победил, за исключением потоковой передачи данных. Потоковая передача была немного хуже по сравнению с REST. gRPC также показал лучше производительность при отправке данных, чем при получении. Я предполагаю, что это связано со сжатием заголовков HTTP/2, но мне еще предстоит это проверить, проанализировав данные HTTP.
Я также запустил этот бенчмарк на Windows и получил аналогичные результаты. Однако в Windows весь тест выполнялся на минуту дольше.
Протестируйте сами
Если вы хотите повторить тест самостоятельно или подстроить его под себя, то не стесняйтесь клонировать мой репозиторий EmperorRXF/RESTvsGRPC
Для теста запустите оба сервиса, как показано ниже.
Запуск RestAPI: dotnet run -p RestAPI -c Release
Запуск GrpcAPI: dotnet run -p GrpcAPI -c Release
И запустите бенчмарк.
Запуск бенчмарка: dotnet run -p RESTvsGRPC -c Release
Выводы
Для этой конкретной полезной нагрузки gRPC примерно в семь раз быстрее REST при получении данных и в десять при отправке. В основном это связано с плотной упаковкой Protocol Buffers и использованием HTTP/2 для gRPC.
Однако на реализацию этого простого gRPC сервиса я потратил около 45 минут и всего 10 минут на создание WebAPI. Это связано с тем, что REST давно уже стал мейнстримом, и в большинстве популярных фреймворков (в том числе, в ASP.NET Core MVC) есть встроенная поддержка для быстрой реализации подобных сервисов (с помощью соглашений и шаблонов). Но и gRPC, хотя и не так сильно, тоже продвинулся вперед с тех пор, как я использовал его в последний раз, поскольку на тот момент мне пришлось вручную запускать компилятор protoc, а теперь он встроен в Visual Studio в процесс сборки. Все, что мне нужно было сделать, это использовать ItemGroup "Protobuf" в csproj-файле. Кроме того, мы можем ожидать более тесной интеграции gRPC в ASP.NET Core 3.0.
Для меня это хорошие цифры, чтобы перевести свои микросервисы на gRPC.
Узнать подробнее о курсе "Highload Architect".
Смотреть открытый вебинар на тему «Репликация как паттерн горизонтального масштабирования хранилищ»
Junecat
Спасибо за подробный перевод! Давно уже было интересно, насколько современный protobuf легок в использовании. Автор оригинальной статьи пишет, что потратил немного времени на реализацию простого сервиса, причем нужные инструменты интегрированы в VS. Немного смущает только то, что в статье речь о NET Core 3.0 идет как о самой новой версии, а вокрег уже вовсю используется следующая версия, пятерка
ialexander
Так ведь оригинальная статья от 3 апреля 2019 года.