Массовый переход от монолитов к микросервисам решает ряд проблем:

  • раздельный деплой и рефакторинг;

  • удобное масштабирование частей системы;

  • прозрачное разграничение ответственности команд;

  • снижение бласт-радиуса;

  • снижение когнитивной нагрузки на разработчика.

При этом создает другие проблемы: взаимодействие сервисов существенно сложнее и дороже, чем взаимодействие объектов в памяти. Частично упростить его можно с помощью протокола gRPC.

gRPC дает возможность зафиксировать в репозитории контракты межсервисных вызовов, строгую типизацию, стриминг, кросс-платформенную кодогенерацию и множество других полезных для межсервисного общения вещей.

Из этой статьи вы узнаете, когда стоит применять gRPC, а когда лучше воздержаться, как решаются типичные задачи, включая конфигурирование, отладку, healthcheck, а также то, о чем умалчивает документация. 

По материалам выступления на конференции DotNext:

Меня зовут Михаил Кузнецов, сейчас я senior engineering manager платформы автоматизации маркетинга Mindbox.

Основы gRPC: что это такое и какие проблемы решает

Что такое gRPC. gRPC — кросс-платформенный протокол удаленного вызова процедур от Google. Транспорт осуществляется по HTTP/2. Неплохо поддерживается в .NET Core, начиная с версии 3.1. В версии .NET 5.0 появилось много расширений и дополнительных настроек — всё стало еще лучше.

Какую проблему решает. В течение последних лет многие компании стараются уйти от монолитов к микросервисам. А в ответ получают много сетевых вызовов, у которых есть ряд недостатков:

  • Сложнее с точки зрения кода. Если экземпляр класса А вызывает экземпляр класса В, то это достаточно тривиально. Если это сетевой вызов, надо сериализовать пейлоад и получить ответ, в котором возможны сетевые ошибки. Этот пейлоад также нужно отправить в канал и получить из канала. В общем, требуется достаточно много действий.

  • Хуже перформанс. Вызвать function call намного дешевле, чем гонять параметры и результаты работы по сети. 

  • Усложняется обработка ошибок. Появляется сразу несколько дополнительных слоев, где что-то может пойти не так. Например, сериализатор сериализовал не так (формат даты / числа с точкой вместо запятой / snake_Case вместо PascalCase), или «икнула» сеть, или сервис не отвечает, или сервис отвечает, но слишком долго.

В качестве альтернативы синхронного взаимодействия иногда возникает желание использовать шины данных, брокеры (Kafka, RabbitMQ и аналоги), потому что зачастую они уже есть в инфраструктуре и проблему кое-как решают. Этот подход не очень удобный: асинхронный eventual consistency-вариант вместо синхронного вызова — это костыль. 

Чем gRPC лучше REST. У REST сразу несколько недостатков:

  • REST — не Contract First: сложно управлять контрактами и целостностью API. По сути целостность контрактов нельзя контролировать. Можно прикрутить Swagger, инструмент для генерации документации по коду. То есть у вас уже есть код и генерируется документация. А хотелось бы по понятным причинам наоборот: чтобы код генерировался по документации.

  • Много бойлерплейт-кода: создать HTTP-клиент, HTTP client factory, договориться о сериализации (JSON, XML или другие варианты), учесть различные форматы дат, чисел и так далее, кейсинг. Соответственно, много возможностей для возникновения ошибок — нужно писать DTO-классы специально для транспорта.

  • У REST не очень хороший перформанс в сравнении с gRPC.

  • Использовать REST не очень оптимально с точки зрения сети, потому что будет летать JSON или XML. Правда, этот момент можно улучшить: использовать msgPack или похожие средства оптимизации транспорта.

Что предлагает gRPC

Contract First. Сначала договариваемся о том, что и куда пересылаем, описываем это строго в protobuf-файле, кладем в репозиторий, и, по сути, с этого момента он зафиксирован. При желании от этого решения можно отказаться и пользоваться Code First. Но это применимо, только если у вас весь бэкенд на .NET: можно создать те самые DTO, повесить на них атрибуты привычным вам способом, как с тем же JSON или XML. 

Строгая типизация. Если вы пишете на .NET, скорее всего, любите строгую типизацию —  здесь она есть :) 

Автогенерация клиентского и серверного кода. Описываете контракт, используя штатный инструмент во время билда, и у вас создается либо клиент, либо сервер, либо и то и другое — в зависимости от настройки. 

Это дает сразу несколько преимуществ:

  • При кодогенерации не нужно писать код самому и поддерживать его. 

  • Нельзя сбилдить проект, не поменяв клиент и сервер, если поменялся контракт. 

Контракт может развалиться только в одном сценарии: у вас два сервиса (клиент и сервер), и у них разъехались версии, то есть один перебилдился, обновился и задеплоился, а второй — нет. Понятное дело, с этим сложно что-то сделать — только тестировать. Во всех остальных сценариях на билде перегенерируется весь соответствующий код — контракт не развалится.

  • Встроена сериализация из коробки.

Она достаточно эффективна и с точки зрения перформанса, и с точки зрения транспорта: пейлоад, который летает по сети, существенно компактней обычного JSON. 

  • У gRPC достаточно лаконично сделана обработка ошибок. Какие-то вещи она маскирует и упрощает, но для практического применения ее вполне достаточно. 

  • gRPC предлагает удобный стриминг, то есть возможность создать стрим в любую сторону: с сервера на клиент, с клиента на сервер и даже двунаправленный стрим — дуплекс. 

Поддержка в .NET

Библиотека gRPC C# 

Это достаточно тонкая обертка вокруг unmanaged-библиотеки. Есть основная библиотека на С++ и оболочка для вызовов на .NET. Она обладает максимальной функциональностью и минимальными удобствами. 

Как в любой обертке, из нее торчат уши. Например, все настройки сделаны не в виде строго типизированных свойств с соответствующими ограничениями, а в виде строковых констант. То есть вы передаете строку с названием настройки и строку с ее значением. У вас также будет unmanaged-код, которого обычно хочется избежать: ошибки в unmanaged-коде вызваны как возможными багами самой библиотеки, так и неправильным использованием. Эти ошибки хуже отлавливаются, хуже дебажатся и так далее.

Использовать библиотеку я советую местами, не в продакшене, например писать эмуляторы того, что потом будет в продакшене, подключать для мокирования, для тестов. Стартануть на ней максимально просто, это делается буквально в одну строку.

Альтернативная библиотека gRPC for .NET

Это мейнстримное решение, поддерживаемое Microsoft, — оно подробно описано в документации. Тут быстрый старт немного сложнее, но есть и плюсы: она полностью управляемая. Всё, что вы настраиваете и конфигурируете, строго типизовано. Эта библиотека очень хорошо интегрируется в пайплайны ASP .NET Core, то есть в DI многие вещи получится сделать привычным образом. Дальше рассказывать я буду в основном о ней, потому что это мейнстримное решение.

Транспорт происходит по HTTP2 — не будет сюрпризом, что внутри gRPC-клиента сидит знакомый всем System.Net.Http.HttpClien. Многие настройки транспортного уровня находятся в привычных для всех полях и классах, которые составляют HTTP-клиент. Если эта настройка не выглядит gRPC-специфичной, то, скорее всего, вы найдете ее там же, где и раньше. Это различные таймауты, Keep Alive, авторизация и тому подобные вещи.

Многие серверные gRPC-настройки также настраиваются стандартным способом, как и настройки Kestrel. Если вы делаете gRPC-сервер с помощью gRPC for .NET в .NET-приложении, то конфигурацией Kestrel вы также управляете поведением gRPC-сервера.

Protobuf-контракты

Выглядит это примерно так:

message Outcome{
  string id = 1;
  int32 selection_kind = 2;
  double price = 3;
  bool disabled = 4;
  map<string, string> properties = 5;
  string raw_id = 6;
}

Контракт protobuf строго типизованный и строго определяет последовательность полей. Это нужно, например, по причинам совместимости старых и новых версий API. Он поддерживает nullable, коллекции, enum, дефолтные значения, вложенные типы. 

Немного чисел: на официальном сайте Google заявляет, что контракт protobuf до 10 раз компактнее, до 100 раз быстрее JSON в сыром виде. Могу сказать, что это похоже на правду: по замерам он кратно компактнее и в 20–30 раз быстрее.

Информация для любителей MessagePack: по бенчмаркам скорость и компактность — на неразличимо близком уровне.

Кросс-платформенность и кодогенерация. Генерируются и клиент, и сервер, и сериализация, и десериализация, и DTO, то есть весь этот слой будет у вас готов — его не нужно писать.

Поддерживается несколько популярных платформ: C#, Go, Java, Python, Kotlin, PHP, Ruby. Поддерживается не только бэкенд — стандартные стеки, но и клиентские мобильные платформы.

Отдельно стоит отметить, что, по опыту, gRPC снимает проблемы с кейсингом, форматированием, опечатками в именах полей. Снимает также проблемы с форматами примитивных типов: дат, чисел и так далее. Это особенно актуально, если на бэкенде существуют разные технологические стеки. Естественно, все ошибки можно отловить на тестировании, но проще вообще не бороться с ними — кодогенерация одним инструментом решает эту проблему.

Код обоих сервисов: и клиента, и сервера будет синхронизирован с их контрактом. Это гарантируется компилятором. С вашей стороны нужно только гарантировать, что если вы поменяли контракт, на стенд выкатываются синхронизированные версии. Это можно обеспечить, например, интеграционными тестами. Все, что уровнем ниже, может быть проверено на compile time.

Альтернативы gRPC

Kafka. Основной минус — если есть topic, на который вы подписываетесь, то вы не можете фильтровать данные и сообщения, которые туда падают. Приходится читать все и разбирать на клиенте.

Приведу пример. Предположим, у нас есть клиент. Он знает ID пользователей и хочет получать информацию только по тем пользователям, которые сейчас онлайн. Очевидно, что онлайн у нас малое количество от всех пользователей, которые есть в базе.

Как это можно было бы сделать с помощью топиков?  Сервер отслеживает, кто сейчас онлайн, и скидывает информацию о них в топик, отфильтровывая обновления данных по тем пользователям, которые офлайн.

При этом, если разные клиенты хотят читать про конкретных пользователей, то им всё равно нужно будет выбирать, либо дискардить кучу сообщений, либо заводить по топику на пользователя или по топику на хэш от ID пользователя. А это уже динамические топики, что достаточно сложно и дорого. Если вы пробовали делать тысячи партиций, а топик — это минимум одна партиция в Kafka, то, возможно, уже сталкивались с этой проблемой. 

Кроме того, создать и удалить топик — достаточно ресурсоемкая операция, а подписаться на новый топик занимает несколько секунд. В итоге получается очень шаткая и непонятно зачем нужная конструкция. Как это выглядит со стримингом gRPC? Мы создаем стрим типа GetUserInfoById, передаем ID, в ответ получаем стрим по конкретному пользователю.

Если тот, кто кормит нас данными (продюсер), сломался и перестал обновляться, то в случае с Kafka нужны какие-то внешние инструменты, возможно, healthchecks продюсера. Их нужно отслеживать — получается достаточно сложная и хрупкая конструкция. В случае с gRPC все проще: разрывается соединение, клиент узнает об этом очевидным образом и может среагировать на проблему. 

OpenAPI. Предлагает контракты: они не обязательны, но их можно использовать. Позволяет осуществлять кодогенерацию клиента, но так как это не gRPC, а сырой HTTP, у него несколько хуже перформанс, нагрузка на сеть несколько выше, а со стримингом все несколько сложнее. Например, там вообще нет дуплекса. В остальном OpenAPI во многом схож с gRPC.

HttpClient.GetStreamAsync. У него тоже есть стриминг, но единственное, что про него можно сказать, — им можно что-то стримить. Остальные потребности он не закрывает: остаются все те же проблемы с типизацией, с контрактами, сериализацией. Все нужно делать вручную, да и сам стрим тоже нужно обслуживать вручную. В общем, вариант достаточно спорный.

Подводные камни gRPC

Издалека все выглядит хорошо, а на деле возникают сложности. Ниже минусы в первом приближении:

Из-за бинарного формата пейлоад не читается человеком. Существуют инструменты, которые умеют читать пейлоад, но отладка немного усложняется. 

Не интегрируется с браузерами. Эту проблему можно решить сторонними проектами, их уже два. Первый называется gRPC HTTP API — это, по сути, генерация HTTP end point и контроллера поверх gRPC endpointer. Второй проект называется HTTP Gateway — это отдельно стоящий сервис, который работает как прокси для gRPC-сервиса.

Несмотря на то что с браузерами gRPC не интегрируется, с другими клиентами, то есть с мобильными, он интегрируется и, судя по отзывам, достаточно хорошо.

Нет специальной поддержки бродкаста. В примере с получением информации о пользователе, который мы рассматривали выше, в ситуации «все хотят информацию по одному пользователю», потребовалось бы столько стримов, сколько клиентов захотело получить информацию. Никакой хитрой транспортной оптимизации нет. На сервере и запросы, и стримы независимые. 

HealthCheck gRPC-сервиса

Если вы пользуетесь Kubernetes, то знаете, что такое HealthChecks. Если коротко, это endpoints, через которые можно опросить сервис и приблизительно понять, что с ним происходит.

Есть так называемые Liveness/Readiness probes. Первая отвечает на вопрос, жив ли сервис, а вторая — готов ли он обрабатывать наши запросы. Даже если у вас нет Kubernetes, такие эндпойнты могут быть полезны для внешнего мониторинга и логирования состояния сервиса. 

Для gRPC-сервисов есть стандарт. Он описан прямо в основном репозитории gRPC. Пакет, реализующий этот стандарт по .NET, называется gRPC HealthCheck. Он несложно устроен: чтобы иметь у себя хелсчеки, надо подключить пакет и реализовать интерфейс. Все, что нужно сделать, когда хотим поменять статус сервиса, — выставить статус специальному объекту healthGrpcService. Статусов всего четыре вида: serving, not_serving, unknown и service_unknown.

Первый параметр string.Empty — название сервиса для ситуации, когда в одном процессе находится несколько независимых gRPC-сервисов. Управлять их статусами можно независимо. Насколько это бьется с микросервисной архитектурой — вопрос. Но инструмент для такой ситуации есть. Вместо string.Empty можно передавать имя сервиса — в пробах будут пропагироваться сервисы, соответствующие своим статусам. 

gRPC и KeepAlive 

C KeepAlive все оказалось не так просто. Здесь тоже есть стандарт в виде таблицы с описанием: 

gRPC_ARG_KEEPALIVE_TIME_MS — время, которое проходит между KeepAlive-пингами. По дефолту клиент ждет бесконечно долго: он вообще ничего не шлет, а сервер шлет пинги раз в два часа. Очевидно, это сделано для того, чтобы убивать уснувших или отвалившихся клиентов.

gRPC_ARG_KEEPALIVE_TIMEOUT_MS — время, которое ожидается между тем, как был послан пинг и получен успешный ответ. 20 секунд — достаточно адекватный период. 

gRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS — по умолчанию 0. Это означает, что, если у вас не происходит отправок бизнес-сообщений, то и KeepAlive слаться не будут. Если указан 1, то KeepAlive слаться будут. 

gRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA. Регламентирует максимальное количество KeepAlive-пингов, не перемежающихся бизнес-сообщениями. 

Резюмируем: по дефолту ничего не шлется. Если сделаем time не бесконечным, а сколько-то секунд, например одну минуту, то раз в минуту у нас будут слаться пинги, но только если происходят постоянные бизнес-вызовы. Если стрим висит достаточно долго, то все равно ничего не будет посылаться. Если мы разрешим отправку, выставив третий параметр в единицу, то пошлется два пинга и после этого опять KeepAlive остановится. 

Здесь достаточно неприятный момент: если мы хотим это изменить, то придется передеплоить все серверные gRPC-части. Если мы увеличим gRPC_ARG_HTTP2_MAX_PINGS_WITHOUT_DATA только у клиентов с 2 на 10 или на бесконечность, то после второго сообщения непереконфигурированный сервер будет прекращать обрабатывать стримы, посылать сообщение GOAWAY, и на этом все будет заканчиваться. С точки зрения менеджмента и деплоя, достаточно сложное изменение. Нужно задеплоить все сервера, потом всех клиентов. А если вы хотите что-то поменять или откатить, то придется делать все то же самое, только в обратном порядке.

Инструментарий

При разработке REST API многие пользуются Postman или аналогичными инструментами. Естественно, для gRPC хотелось бы иметь что-то похожее.  

Есть BloomRPC. Мы пробовали искать что-то еще, потому что в сравнении с Postman он выглядит слишком примитивно для главного инструмента, решающего задачи отладки сервисов. Но другого инструмента не нашлось. Базовые потребности он закрывает. Так как gRPC работает с протоконтрактами, работа с BloomRPC достаточно простая и удобная. Загружаем протофайл — это сразу дает возможность вызвать конкретный сервер, выбрать endpoint. У нас все готово для того, чтобы вбить значение в пейлоад. Нужно только указать URL, куда мы будем коннектиться. 

Немного о производительности

Я говорил о Protobuf, но это не все, что определяет протокол. Есть еще затраты в .NET-коде: распарсить, сериализовать, послать, получить и так далее. Я намеренно не хотел синтетики, числа будут из реального приложения, но достаточно легковесно обрабатывающего каждое конкретное сообщение. 

Накладные расходы на gRPC даже на фоне легковесной обработки практически не измеримы. Это проценты от миллисекунд. С другой стороны, минимум сотни сообщений, обработанные на одном ядре в секунду. Помимо самого gRPC, происходит бизнес-обработка, мы что-то складываем в Mongo, продьюсим в Kafka, что-то считаем. С учетом накладных расходов на gRPC мы наблюдали сотни сообщений в секунду, что нас вполне устраивало. 

Мы столкнулись с проблемами не самого gRPC, а LargeObjectsHeap: если посылать действительно большие сообщения, большие пейлоады, у вас будет фрагментироваться LOH и будут происходить частые сборки мусора во втором поколении. С этим ничего не сделать, только уменьшать размер сообщения до вменяемого, бить на чанки, делать рефакторинг, иногда что-то менять по бизнесу. 

Еще в обновлении библиотеки появился extension class UnsafeByteOperations. Он позволяет хотя бы на клиенте не копировать большой пейлоад в большой пейлоад формата gRPC, который потом полетит по сети, а взять готовый массив с большим пейлоадом, легковесно обернуть его и передать. На мой взгляд, это все равно достаточно слабое решение, потому что для серверной части UnsafeByteOperationsвообще нет. Скорее всего, проблемы останутся. 

Мы поднимали две ноды, клиентскую и серверную, большое количество стримов, эмулировали некоторую нагрузку. Удавалось параллельно выдержать несколько десятков тысяч gRPC-стримов, которые еще и посылают сообщения, а не просто висят мертвым грузом, но посылают их не слишком часто, раз в несколько секунд. Тем не менее десятки тысяч стримов между нодами работали хорошо. Ни по памяти, ни по CPU безумных чисел не было, речь шла о нескольких ядра и паре Гб в режиме работы, то есть когда нагрузка спадает, то и расход памяти существенно сокращается. 

Подведем итоги

Если у вас .NET Core 3.1 и выше, микросервисы на бэкенде, они написаны на разных стеках и всё это причиняет боль, стоит рассмотреть gRPC, оценить его применимость для вашего проекта. Возможно, он будет вам полезен. 

Следующая .NET-конференция DotNext пройдет онлайн 7 и 8 апреля.

Комментарии (37)


  1. bezarius
    17.01.2022 13:20

    Добавлю от себя небольшой рецепт счастья: Blazor wasm + gRPC(code first) - это очень удобно. Ну или по крайней мере мой опыт был довольно успешным :)


    1. LbISS
      18.01.2022 10:07

      Blazor wasm сразу вызывает 2 опасения - 1) как на это искать людей 2) как это диагностировать, если что-то отвалилось вне девелоперской сети. Всё в бинарке и wasm, и код - и запросы.

      А так, конечно, всегда соблазнительно вместо js перетянуть какой-то "более правильный" язык на фронт.


      1. bezarius
        18.01.2022 11:38

        1) своих переучить, технология довольно простая в освоении

        2) там довольно понятные стектрейсы в случае ошибок. Ловушки с читабельными исключениями тоже доступны.


  1. aftertherainbow
    17.01.2022 13:34

    асинхронный eventual consistency-вариант вместо синхронного вызова — это костыль. 

    HTTP Gateway — это отдельно стоящий сервис, который работает как прокси для gRPC-сервиса.

    Ладно


    1. v0stok86
      19.01.2022 15:32

      Ну я так понял про HTTP Gateway это было в контексте протаскивания протобафа на эндпоинты, т.к. протобаф в браузер не протащишь (пока). И это тоже костыль, и автор вроде нигде не писал что этот гейтвей - хорошее решение. Скорее это надо читать так: хорошего решения сейчас нет, но, как костыль, есть вот этот гейтвей если очень хочется


  1. sebasww
    17.01.2022 14:29
    +1

    То есть, только для высоких нагрузок? Проблема с совместимостью протокола, поддержки и тестирования пересилитвают скорость реста? Правильно я понял?

    Если в системе только c#, имеет ли смысл использовать сразу Orleans + протобуф?

    Есть ли вообще какая-то мало малькая инфраструктура вокруг сабжа? TTL, кластеризация, например?


    1. Mijka64 Автор
      17.01.2022 16:34

      Не совсем понял первый вопрос. Для высоких - хорошо, для невысоких - вроде б тоже хорошо, т.к. более "надёжно" всё получается - через общий контракт, кодогенерацию и тд.

      Вопрос про Orleans - тут за скоуп выходит, можно использовать, можно не использовать, это ж фреймворк, определяющий как будет выглядеть довольно толстый слой кода, а gRPC - только транспорт) В этом смысле ответ на вопрос про Orleans зависит от домена - что вообще за система, а gRPC где угодно можно использовать.

      Про инфраструктуру - лучше каждую конкретную фичу гуглить. Например, TTL в некотором виде есть (т.н. deadlines), но его поведение с grpcStreams нас скорей удивило.


  1. Scratch
    17.01.2022 14:42
    +5

    Стоит упомянуть gRPCurl, очень удобная штука для всяких автотестов


    1. blanabrother
      17.01.2022 16:33
      +3

      И grpcui. Недостаток утилит конечно в том, что при отсутствии рефлексии в grpc сервисе, надо ручками с помощью protoc компилировать protoset файлы из proto файлов контрактов. Обычно ок, тоже можно автоматизировать.


      1. Scratch
        17.01.2022 18:47

        Мы просто proto файлы скармливали безо всякой компиляции, всё работало


      1. justmara
        17.01.2022 22:22

        в тест среде можно деплоить с HttpApi - получая rest+сваггер обёртку над этим grpc, которую можно дёргать по-старинке курлом или из браузера.


    1. Mijka64 Автор
      17.01.2022 16:38
      +1

      Спасибо. Мы тогда пользовались BloomRPC, в видео есть, а из статьи вырезал чтоб не раздувать пост.


  1. blanabrother
    17.01.2022 16:38
    +2

    Если честно, то не понял сравнения с Kafka. Как можно сравнивать синхронный (gRPC, REST, JsonRPC и прочие) и асинхронный (очереди [*MQ] и журналы [Kafka]) принципы взаимодействия? Совершенно разные подходы не просто в коде, а вообще во всей системе и логике.


    1. Mijka64 Автор
      17.01.2022 19:25

      Всё так. Но

      • Неоднократно встречались попытки натянуть sync over async.

      • Grpc streams не request-response штука и в определённых сценариях может быть альтернативой


    1. kayan
      17.01.2022 20:14
      +2

      Синхронность и асинхронность мало общего имеют с очередями и интернет-протоколами. Они связаны с особенностями потока управления.

      Кафка вполне может использоваться для синхронного взаимодействия. Grpc - для асинхронного.

      Их корректно сравнивать как средство доставки сообщений, например.


      1. Mijka64 Автор
        18.01.2022 20:53

        А можете как-то кратко описать кафку для синхронного и чтоб это не было мрачно-костыльно?


  1. gdt
    17.01.2022 18:59

    Что насчет WCF? Одно время хотели отказаться от него в пользу gRPC, учитывая, что protobuf в проекте уже есть — однако в итоге до этого дело не дошло. Интересно, какие у вас соображения на этот счет?


    1. granit1986
      17.01.2022 19:34
      +1

      Работаю в букмекерской конторе - у нас было, что сайт вешает ядро на WCF запросами в какие-то моменты - вырастает количество потоков и всё умирает. Перевели все API на grpc и всё стало хорошо


    1. impwx
      17.01.2022 19:50
      +2

      WCF не портирован в .NET Core, насколько я знаю


      1. granit1986
        17.01.2022 19:53

        Не портирован, но и упоминаний про него небыло нигде.

        Вообще есть сторонний порт на каких-то зачаточных стадиях, но чисто для переноса. Лучше с WCF переписать


      1. gdt
        17.01.2022 20:01

        Это проблема, да. Есть конечно Core WCF, но его в продакшн не затянешь


        1. kotov_a
          18.01.2022 10:30
          +1

          Core WCF уже можно в продакшн, если вам хватает имеющихся фичей. Пока только начинают делать генерацию WSDL, но в целом решение живое. В своих проектах и в обсуждениях на гитхабе критичных проблем не видел.


    1. Mijka64 Автор
      18.01.2022 01:57
      +3

      Мы отказались от остатков WCF году в 2014-2015 ещё. Наш опыт под высокой нагрузкой - крайне негативный. С учетом того что мы 80% уже перевели на .NET Core и хотим дожать в 2022Q1, рад что сейчас не надо портировать ещё и это!)


  1. kayan
    17.01.2022 20:10
    +4

    Преимущества и недостатки gRPC изначально связаны с парадигмой взаимодействия.

    Если предполагается restful взаимодействие с понятной предметной областью - то классический Rest однозначно лучше. В его соглашениях все взаимодействия описаны, и есть много средств, успешно их использующих.

    А если требуется RPC в произвольной форме - тогда, конечно, grpc на данный момент в общем случае самый логичный первый выбор.

    P.S. Очень жаль, что не рассказали о том, что протобаф доступен без всякого grpc и протофайлов как форматтер в "обычном" рест-взаимодействии. Может сложиться впечатление, что для решения всех описанных проблем обязательно тянуть весь grpc.


    1. mkll
      18.01.2022 17:13
      +1

      Если предполагается restful взаимодействие с понятной предметной областью - то классический Rest однозначно лучше.

      Звучит так, как будто restful-взаимодействие — самоцель. Но обычно нужно просто взаимодействие, а к restful обращаются потому, что это первое, что приходит в голову.

      Ну т.е. можно сказать ведь, что "если предполагается взаимодействие через TCP-сокеты, то классическая работа с сокетами однозначно лучше". Трудно спорить. ;)

      Очень жаль, что не рассказали о том, что протобаф доступен без всякого grpc и протофайлов как форматтер в "обычном" рест-взаимодействии.

      Без gRPC — да.

      Без протофайлов — нет. Ну т.е. можно "потом" руками модифицировать уже сгенерированный исходный код и брать на себя всю ответственность за нарушения контракта, но в первоначальной итерации все-равно нужно компилировать протофайлы.


      1. cstrike
        18.01.2022 17:52
        +1

        Не правда. Можно и без протофайлов

        https://github.com/protobuf-net/protobuf-net.Grpc


        1. mkll
          18.01.2022 20:41
          -1

          Гм. Мне это почудилось, что между "можно без протофайлов" и "можно без протофайлов, но с внешней примочкой" есть неуловимая смысловая разница? Или мне не почудилось, и она действительно есть?


          1. cstrike
            19.01.2022 10:58

            А не майкрософтовские неймспейсы у нас не в почете?


            1. mkll
              19.01.2022 21:17
              -1

              Ну слушайте, я завтра (сегодня я занят) заявлю, что для компиляции, скажем, языка С, не нужен компилятор с языка С, пороюсь в загашниках и достану какую-нибудь приблуду, которая Сшный код конвертирует в ассемблерный.

              Буду ли я прав? Буду, если сделаю оговорку, что компилятор не нужен не всегда. Без такой оговорки я буду неправ, поскольку в этом случае мое утверждение будет претендовать на то, что оно справедливо во всех случаях. Автор утверждения, которое я оспаривал, такой оговорки не сделал.

              Поэтому давайте не будем общее утверждение подтверждать частными случаями? Приведенная выше примочка не является штатным средством, не входит в спецификацию protobuf, к тому же реализована только для .NET, а вовсе не для всех языков, для которых есть protobuf.

              А не майкрософтовские неймспейсы у нас не в почете?

              Не понял вопроса.


  1. fkafka
    18.01.2022 06:12
    +3

    REST — не Contract First: сложно управлять контрактами и целостностью API. По сути целостность контрактов нельзя контролировать. Можно прикрутить Swagger, инструмент для генерации документации по коду. То есть у вас уже есть код и генерируется документация. А хотелось бы по понятным причинам наоборот: чтобы код генерировался по документации.

    Здрасте, приехали. Сваггер это умеет чуть ли не от рождения. Описываешь API на JSON или YAML (Open API) потом уже с него генерируешь и сервер (бойлерплейт) и клиент.


  1. cstrike
    18.01.2022 09:20
    +3

    Postman Now Supports gRPC

    https://blog.postman.com/postman-now-supports-grpc/


  1. LbISS
    18.01.2022 10:10
    +1

    Мне кажется одна из больших "болей" в микросервисах - это версионирование, жаль, что в статье это не затронули. По rest уже есть некие стандартные приёмы, библиотеки, типа делаешь /v2/ и дальше двухшаговая миграция. А как это работает с gRPC и protobuf? Есть какой-то стандарт, что делать с контрактом при необходимости сделать несовместимые изменения? Просто генерим второй контракт и дублируем все затронутые классы? Есть ли набор ошибок передающих статус deprecated?


  1. vba
    18.01.2022 11:56

    Никогда не понимал зачем(ну кроме очень узких задач) нужен grpc для микросервисов(сразу вспоминаю про корень всех зол). Но на примере .NETa использование grpc выглядит абсурдным, ведь майки для таких узких задач специально создавали wcf. Наверное wcf от гугла звучит круче чем от майков.


    1. LbISS
      18.01.2022 12:14
      +4

      Wcf очень многословен, использует как базу по умолчанию тяжёлый и медленный xml. И вас не смущает, что его развитие по сути было остановлено 12 лет назад? По сути можно ещё предложить сейчас писать веб приложения на вебформс. Ушло уже время, ушло, это медленные, неудобные, неподдерживаемые технологии, которые не построены на современных принципах.


      1. mayorovp
        18.01.2022 13:23

        Медленные — возможно. Но что не так с удобством и принципами-то?


  1. OkunevPY
    18.01.2022 21:44
    -1

    Статья печалька, много умных слов мало умных мыслей.

    Grpc как и rest работает поверх HTTP, как это ни странно)) Использует HTTP/2 да это хорошо, но он требует SSL, что наводит на мысль что при маленьких запросах затраты на согласование коннектов будут не соизмеримы с временем выполния самого запроса, это надо учитывать.

    То что rest не умеет Contract first, бред полный.

    Подход зависит от инструментов, хотите Contract first будет он, с генерацией не только контроллеров но клиенсткой стороны скажем на TS или каком другом языке.

    openapi описывает контракты и они не обязательны, вы сами то себя слышите?

    То-есть схема вам говорит я жду обект типа А с полями 1 и 2 такого-то типа, вы уверенно пихаете в метод болт и он у вас проходит так что-ли получаеться? Ну если у вас так продукт построен то мне жаль деньги потраченные на его развитие.

    Senjor - это звучит конечно круто, но по сути изложения статьи я бы к себе на проект и джуном не взял.


    1. cstrike
      19.01.2022 11:10

      но он требует SSL

      Это не обязательно, хотя и приветствуется:

      If an HTTP/2 endpoint is configured without TLS, the endpoint's ListenOptions.Protocols must be set to HttpProtocols.Http2. An endpoint with multiple protocols (for example, HttpProtocols.Http1AndHttp2) can't be used without TLS because there's no negotiation. All connections to the unsecured endpoint default to HTTP/1.1, and gRPC calls fail.

      Если настроить сервер на Http2 only то можно и без TLS

      .ConfigureWebHostDefaults(webBuilder =>
      {
          webBuilder.UseStartup<Startup>();
          webBuilder.ConfigureKestrel(options =>
          {
              options.ConfigureEndpointDefaults(listenOptions =>
              {
                  listenOptions.Protocols = HttpProtocols.Http2;
              });
          });
      })