gRPC — это платформа с открытым исходным кодом для удаленных вызовов процедур.
В Яндекс.
Маркете gRPC используется как более удобная альтернатива REST. Сергей Федосенков, возглавляющий службу разработки инструментов для партнеров Маркета, поделился опытом использования gRPC в качестве протокола для построения интеграций между сервисами на Java и C++.
Из доклада вы узнаете, как избежать распространенных проблем, если начать использовать gRPC после REST, как возвращать ошибки, реализовывать трассировку, отлаживать запросы и тестировать клиентские вызовы.
В конце Существует неофициальная стенограмма доклада.
— Для начала хотелось бы познакомить вас с некоторыми фактами о Яндекс.
Маркете, они пригодятся в рамках отчета.
Первый факт: мы пишем сервисы на разных языках.
Это накладывает требования на доступность клиентов к услугам.
А если у нас есть сервис на Java, то было бы неплохо, чтобы клиент для него был, например, тоже плюсовой или Python.
Все наши услуги независимы; крупных релизов всего Маркета не запланировано.
Микросервисы выпускаются независимо, и здесь нам важно, чтобы была обратная совместимость и чтобы протокол ее поддерживал.
Третий факт: у нас есть как синхронная, так и асинхронная интеграция.
В докладе я в основном буду говорить о синхронности.
Что мы использовали? Теперь, конечно, ядром нашей интеграции являются REST или REST-подобные сервисы, которые обмениваются XML/JSON по протоколу HTTP 1.1. Еще есть XML-RPC — мы его в основном используем при интеграции с кодом Python, то есть в Python есть встроенный XML-RPC-сервер.
Там его достаточно удобно развернуть, и мы это поддерживаем.
Когда-то у нас была CORBA. К счастью для нас, мы отказались от этого.
В настоящее время это в основном REST и XML/JSON через HTTP.
Синхронная интеграция имеет проблемы с существующими протоколами.
Мы сталкиваемся с такими проблемами и пытаемся их решить с помощью gRPC. Что это за проблемы? Как я уже говорил, я хочу иметь клиентов на разных языках.
Желательно, чтобы вам не приходилось писать их самостоятельно.
И вообще, было бы круто, если бы клиент мог быть как синхронным, так и асинхронным, в зависимости от целей пользователя сервиса.
Мне также хотелось бы, чтобы протокол, который мы используем, хорошо поддерживал обратную совместимость: это очень важно для параллельных независимых выпусков.
Все наши релизы обратно совместимы, мы не нарушаем обратную связь.
Если что-то сломалось, то это ошибка, и вам просто нужно ее исправить как можно быстрее.
Также необходим какой-то стройный подход к обработке ошибок: все, кто делал REST-сервисы, знают, что нельзя просто использовать HTTP-статус.
Они обычно не позволяют подробно описать проблему; вам придется ввести какие-то свои статусы, свои данные.
В REST-сервисах каждый вносит свою реализацию этих ошибок, и работать с ней каждый раз приходится по-разному.
Это не всегда удобно.
Я также хотел бы иметь управление тайм-аутом на стороне клиента.
Опять же те, кто работает с HTTP, понимают, что если мы на стороне клиента установим таймаут и он закончится, то клиент перестанет ждать завершения запроса, но сервер ничего о нем не узнает и продолжит его выполнять.
Более того, посередине находятся различные прокси, задающие глобальные таймауты.
А клиент может просто ничего о них не знать и настроить их не всегда тривиально.
И, наконец, существует проблема документации.
Не всегда понятно, где взять документацию по REST-ресурсам или некоторым методам, какие параметры они принимают, какое тело можно передавать и как эту документацию донести до потребителей сервиса.
Понятно, что есть Swagger, но и с ним не все тривиально.
гРПК.
Теория Хотелось бы поговорить о теоретической части gRPC — что это такое, какие идеи есть.
А затем перейдем к практике.
В общем, gRPC — это абстрактная спецификация.
Он описывает абстрактный RPC (удаленный вызов процедуры), то есть удаленный вызов процедуры, имеющей определенные свойства.
Сейчас мы их перечислим.
Первая особенность — поддержка как одиночных вызовов, так и потоковой передачи.
То есть все сервисы, реализующие эту спецификацию, поддерживают оба варианта.
Следующий момент — наличие метаданных, то есть чтобы вместе с полезной нагрузкой можно было передавать какие-то метаданные — условно, заголовки.
И — поддержка отмены запросов и таймаутов из коробки.
Также предполагается, что описание сообщений и самих сервисов осуществляется посредством какого-то языка определения интерфейса или IDL. В спецификации также описан проводной протокол HTTP/2, то есть gRPC предполагает работу только через HTTP/2.
Существует типичная реализация gRPC, которая используется в большинстве случаев.
Мы тоже им пользуемся, и сейчас его рассмотрим.
Формат proto используется как IDL. Плагин gRPC для компилятора прото позволяет получить исходный код сгенерированных сервисов из описания прото.
И есть библиотеки времени выполнения на разных языках — Java, C++, Python. В целом поддерживаются практически все популярные языки и библиотеки времени выполнения для них существуют. А сообщения, которыми обмениваются сервисы, представляют собой протосообщения, стилизованные сообщения по схеме protobuf.
Хотелось бы немного остановиться на некоторых особенностях.
Вот они.
Строгая типизация, то есть протосообщение, является строго типизированным сообщением.
Те, кто раньше работал с protobuf, знают, что там можно описывать поля в сообщении с помощью типов.
Существуют типы, как примитивные, так и строковые, байтовые массивы.
Они могут быть скалярными, могут быть векторными.
И, по сути, сообщения могут как поле содержать другие сообщения, что весьма удобно; вообще можно представить любую модель.
По поводу обратной совместимости.
Хотелось бы отметить, что proto IDL — это формат, обратно совместимый «из коробки», то есть он был задуман с учетом обратной совместимости, а Google выпустила версию proto3, которая, по сравнению с proto2, еще больше улучшает обратную совместимость.
Там плюс есть всякие спецификации, как и что можно изменить, чтобы сохранить обратную совместимость в некоторых нетривиальных случаях.
Существует возможность значений по умолчанию, вы можете добавлять новые поля, и потребителю фактически не нужно ничего менять.
Все поля в proto3 являются необязательными и их можно, например, удалить, а доступ к удаленному полю не вызывает ошибок на клиенте.
Еще одна особенность gRPC заключается в том, что клиент и сервер генерируются с помощью компилятора прототипа и плагина gRPC на основе описания прототипа.
Есть возможность в момент написания кода выбрать, какой клиент будет использоваться.
То есть выбирайте асинхронный или синхронный клиент, в зависимости от того, какой код вы пишете.
Например, для реактивного кода очень подходит асинхронный клиент. И такая возможность существует для любого языка.
То есть, как только вы напишете протоописание, после этого вы сможете сгенерировать клиент для любого языка, и вам не придется разрабатывать их отдельно.
Вы можете просто распространять интерфейс своего сервиса в виде протоописания.
Любой потребитель может создать своего клиента.
По поводу отмены запроса и сроков отмечу, что запрос может быть отменен как на сервере, так и на клиенте.
Если мы понимаем, что дальше выполнять запрос не нужно, то можем его отменить.
Есть возможность установить таймаут для запроса.
В gRPC большинство библиотек времени выполнения используют крайний срок как концепцию тайм-аута.
Но на самом деле это одно и то же.
То есть это время, когда запрос должен завершиться.
И что самое интересное, сервер может узнать как об отмене запроса, так и об истечении таймаута и прекратить выполнение запроса на своей стороне.
Это очень круто, думаю, такого больше нет нигде.
По поводу документации хотел отметить, что поскольку IDL для gRPC использует формат прото, то это обычный код. Туда можно писать комментарии, в том числе очень подробные.
И вам нужно понимать, что для того, чтобы ваши пользователи могли интегрироваться с вашим сервисом, они должны иметь этот протоформат дома, и он попадет к ним вместе с комментариями, а не будет находиться где-то еще.
Это очень удобно.
А можно расширить это описание, то есть это настолько удобная функция, что документация идет рядом с кодом, примерно так же, как она может лежать рядом с методами в виде javadoc или каких-либо других комментариев.
Унарный вызов gRPC. Упражняться
Давайте двинемся дальше и посмотрим немного на практику.А самый простой пример использования gRPC — это так называемый унарный вызов, или одиночный вызов.
Это классическая схема — отправляем запрос на сервер и получаем от сервера один ответ. Аналогично тому, как это работает в HTTP.
Давайте посмотрим на пример создаваемого нами эхо-сервиса.
Сервер будет написан на Просе, клиент на Java. Здесь использована классическая схема балансировки.
То есть клиент обращается к балансировщику, а затем балансировщик выбирает конкретный бэкенд для обработки запроса.
Я хотел обратить ваше внимание на то, что поскольку gRPC работает через HTTP/2, используется одно TCP-соединение.
А затем через него проходят различные потоки.
Здесь видно, что соединение между клиентом и балансировщиком устанавливается один раз и остается постоянным, а затем балансировщик балансирует нагрузку на разные бэкенды для каждого вызова.
Если вы посмотрите на это, то происходит вот так и вот так, если сообщения распространяются.
Вот пример кода нашего прото-файла.
Вы можете заметить, что сначала мы описываем сообщение, то есть у нас есть EchoRequest и EchoResponse. Он имеет только одно строковое поле, в котором хранится сообщение.
На втором этапе мы описываем нашу процедуру.
Процедура принимает на вход EchoRequest и в результате возвращает EchoResponse, все достаточно тривиально.
Вот так выглядит описание службы gRPC и сообщений, которые будут отправлены.
Посмотрим, как обстоит дело в случае с плюсами, например.
Собирается в три этапа.
На первом этапе наша задача — сгенерировать источники сообщений.
Это команда, которую мы делаем.
Мы вызываем прото-компилятор, передаем прото-файл в качестве входных данных и указываем, куда следует поместить выходные файлы.
Вторая команда.
Мы генерируем услуги таким же образом.
Единственное отличие от предыдущей команды в том, что мы передаем плагин, и на основе описания, которое есть в формате proto, он генерирует сервисы.
Третий шаг — собираем все это в один бинарник, чтобы можно было запустить наш сервер.
Здесь компоновщику передается дополнительный флаг, он называется grpc++_reflection. Хотелось бы отметить, что у gRPC-сервера есть такая функция — отражение сервера.
Это позволяет вам узнать, какие службы, вызовы RPC и сообщения есть у службы.
По умолчанию он отключен, и вы можете получить доступ к сервису только в том случае, если у вас на руках есть прото-формат. Но, например, для отладки очень удобно, не имея под рукой формата прото, просто включить сервер с функцией отражения и сразу получить информацию.
Теперь посмотрим на реализацию.
Реализация также минималистична.
То есть наша основная задача — реализовать сгенерированный эхо-сервис.
У него есть один метод getEcho. Он просто генерирует сообщения и отправляет их обратно.
Статус ОК – статус успеха.
Далее создаем ServerBuilder и регистрируем в нем наш сервис, который мы спроектировали чуть выше.
Теперь просто запускаем и ждем входящих запросов.
Давайте теперь посмотрим на клиент на Java. Компиляция градиента.
Наша задача — сначала подключить плагин protobuf. Существует базовый набор зависимостей, которые нам необходимо использовать для нашего сервиса; они необходимы на этапе компиляции.
Еще хотелось бы отметить наличие библиотеки времени выполнения.
Для Java в качестве сервера и клиента используется netty, поддерживает HTTP/2, достаточно удобен и высокопроизводителен.
Далее мы настраиваем прото-компилятор.
Для Java нет необходимости устанавливать компилятор локально; его можно взять из артефактов.
То же самое и с плагинами.
Для Java не обязательно иметь его локально.
Вы можете принести артефакт. И важно его просто настроить так, чтобы для всех задач он еще и вызывался, чтобы генерировались заглушки.
Перейдем к Java-коду.
Здесь сначала мы создаем заглушку нашего сервиса.
То есть наша задача для Java — предоставить Channel. В библиотеке времени выполнения есть ChannelBuilder, который мы можем использовать для создания этого канала.
Для простоты мы вручную включили обычный текст, но HTTP2 и gRPC по умолчанию шифруют все и используют TLS. У нас есть заглушка нашего клиента, здесь генерируется синхронный клиент. Таким же способом можно сгенерировать асинхронный клиент, есть и другие варианты.
Далее мы создаем наш запрос протобаффа, то есть конструируем сообщение протобаффа.
Всё, отправляем, вызываем getEcho на нашем клиенте и печатаем результат. Это просто.
Как видите, кода нужно совсем немного, и интеграция построена.
Потоковая передача gRPC. Упражняться
Давайте теперь посмотрим на более продвинутую вещь — потоковую передачу.Я сейчас расскажу вам, как это работает, а позже расскажу, как его можно использовать.
Клиент-сервер потоковой передачи архитектурно выглядит практически одинаково.
То есть у нас есть постоянное соединение между клиентом и балансировщиком.
Дальше начинаются различия.
Суть стриминга в том, что клиент привязывается к какому-то конечному бэкенду, и соединение поддерживается на протяжении всего времени.
То есть вот что происходит дальше.
И просто так.
Здесь хотелось бы отдельно отметить, что использование балансировщика не характерно для стриминга, то есть нужно понимать, что потоковые запросы могут быть довольно долгоживущими.
То есть вы можете их открывать и долго обмениваться сообщениями.
И эти сообщения будут проходить через балансировщик, но, по сути, всегда идти на один и тот же бэкенд. И не очень понятно, зачем оно вообще нужно.
Обычной практикой является, например, когда служба является чисто потоковой или в основном потоковой, тогда используется обнаружение службы.
У gRPC есть точка расширения, в которую можно встроить обнаружение сервисов.
Что нам нужно для реализации потоковых сервисов? У нас одинаковый прото-формат. Мы добавляем еще один RPC, и здесь вы заметите, что мы добавили два ключевых слова перед запросом и перед ответом.
Таким образом, мы объявляем потоки EchoRequest и EchoResponse.
Дальше все становится интереснее.
Наша компиляция никак не меняется в целях создания стриминговых сервисов.
Наша следующая задача — переопределить наш новый метод в нашем сервисе Echo, который будет работать с потоками.
В случае с сервером все несколько проще.
То есть мы можем постоянно читать из потока и можем что-то ответить.
Мы можем реагировать асинхронно.
То есть они независимы, поток на запись и поток на чтение, и здесь все просто для простого сценария.
Вот чтение сейчас, вот запись.
В Java-клиентах дела обстоят немного сложнее.
Никакого синхронного API там использовать нельзя, то есть он просто не работает с потоками.
И он использует асинхронный API. То есть наша задача — реализовать паттерн Observer. Есть интерфейс StreamObserver. Он содержит три метода: onNext, onCompleted и onError. Здесь для простоты я реализовал только onNext. Он только дергается, когда к нам приходит ответ от сервера.
Здесь я просто ставлю сообщения в очередь для обмена сообщениями между потоками.
В чем разница? Вместо блокировки Stub мы просто создаем newStub. Это асинхронная реализация, которая может работать с Observer. На самом деле в Observer можно совершать одинарные вызовы, просто это не так удобно.
По крайней мере, мы не так активно этим пользуемся.
Далее мы создаем наш наблюдатель.
И мы делаем наш вызов RPC. Мы передаем ResponseObserver на вход, а на выходе он дает нам RequestObserver. Затем мы можем совершать вызовы RequestObserver, передавая таким образом сообщения на сервер.
А наш ResponseObserver будет дергаться и обрабатывать сообщения.
Вот пример.
Мы просто звоним.
Вызовите Next и передайте туда Request. Далее из очереди ждем ответа от сервера и распечатываем его.
Я хотел бы обратить ваше внимание на то, что наша задача здесь, как людей, отвечающих за реализацию стриминга, — правильно обработать закрытие этого RequestObserver. То есть в случае ошибки мы должны вызвать на нем метод onError, а в случае успешного завершения, когда мы считаем, что поток можно закрыть, мы должны вызвать метод onCompleted.
Давайте двигаться дальше.
Каковы применения потоковой передачи? Это более продвинутая вещь, не факт, что она будет полезна всем, но иногда используется.
То есть первым делом происходит загрузка и выгрузка каких-то больших объёмов данных.
Сервер или клиент могут выводить данные частями.
Эти части уже можно как-то сгруппировать на клиенте или сервере.
То есть здесь можно делать дополнительные оптимизации.
Схема потоковой передачи также хорошо подходит для отправки сообщений на сервер.
Нужно понимать, что я рассматривал самый крайний вариант, когда у нас двунаправленная потоковая передача.
Или, может быть, поток в одном направлении.
Например, от клиента к серверу или от сервера к клиенту.
В случае сервер-клиент мы можем подключиться к какому-то серверу, и он будет отправлять нам push-уведомления, и для этого нам не нужно будет регулярно проводить опрос.
Следующее преимущество потоковой передачи — привязка к одной машине.
Как я уже говорил, для всех сообщений внутри потока будет установлено одно сквозное соединение, и это соединение будет привязано к одной машине, и оно точно гарантированно никуда не переключится.
Поэтому можно, во-первых, что-то упростить, какую-то межсерверную синхронизацию, плюс можно делать транзакционные вещи.
А двунаправленная потоковая передача (как раз тот пример, который я показал) — это возможность создавать собственные протоколы.
Довольно интересная вещь.
У нас в Яндексе есть внутренние очереди, которые используют двунаправленную потоковую передачу.
А если вдруг у кого-то возникнут такие задачи, то это довольно хорошая возможность этим воспользоваться.
Я также хотел бы обратить ваше внимание на то, что я говорил ранее о метаданных.
А при потоковой передаче метаданные отправляются только в начале и в конце потока.
То есть они отправляются не перед каждым сообщением и после каждого сообщения.
Поэтому, если у вас есть какие-то задачи по метаданным, которые должны отправляться с каждым сообщением, то вам нужно поработать над этим самостоятельно.
Но это всего лишь протоколы.
Здесь gRPC можно использовать в качестве транспорта.
Типовые задачи
Давайте посмотрим, как в gRPC решаются типовые задачи.
Про обработку ошибок я сказал, что есть проблема.
Хотелось бы как-то это унифицировать.
А у gRPC есть подход к унификации.
Это конечно не навязчиво, но в целом наверное приятно пользоваться.
Прежде всего, код ответа в библиотеках времени выполнения является стандартным.
Вот статус, можете им пользоваться.
Для этих статусов существует набор констант, например статус ОК, которые работают достаточно хорошо и поддерживаются в библиотеках времени выполнения.
Например, для Java, если статус ошибочен, выдается исключение.
Для плюсов статус — это просто результат выполнения вызова функции, и вы можете его проверить, а затем действовать в зависимости от него.
Внутри google.rpc.Status есть 3 поля: код ответа, сообщение и подробности.
Существует стандартный набор кодов ответов, которые можно использовать.
Вы можете просто написать нелокализованное сообщение в поле сообщения, чтобы помочь устранить проблему.
Детали — это вектор, в который можно передавать произвольные объекты, в том числе бинарные.
И есть целый набор готовых деталей ошибок, которые можно использовать, они уже проработаны.
Есть подводные камни: например, чтобы в этих внутренних деталях не светились внутренние устройства, скажем, трассировки стека.
Есть рекомендации, что и как использовать.
Здесь возникает резонный вопрос — есть код, есть код и в HTTP, но чем они отличаются? На самом деле они мало чем отличаются.
Аналогично есть BadRequest и так далее.
Но нужно понимать, что здесь есть четкая рекомендация, как сопоставить код с конкретными деталями ошибки, которые необходимо передать.
Есть только стол.
Если вы возвращаете условно BadRequest или что-то еще (там целый набор кодов), то используйте с ним такую-то деталь ошибки.
А можно просто распечатать себе эту таблицу, повесить ее и не изобретать каждый раз велосипед, а взять что-то стандартное.
И все поймут, кто чем пользуется.
Типичные задачи включают отслеживание запросов.
Я опишу проблему.
Например, в микросервисной архитектуре при выполнении фронтального вызова часто вызываются десятки служб.
А если где-то что-то пошло не так, или что-то где-то затормозилось, то не так быстро понять, что происходит. И для решения этих вопросов есть трассировка.
Решение с открытым исходным кодом, например, Zipkin. И конечно, в HTTP это поддерживается через заголовки, здесь через метаданные.
Это хорошее место для отслеживания и хранения атрибутов трассировки.
Атрибуты в метаданных могут быть строковыми или двоичными.
В случае с трассировкой проще использовать строковую трассировку, потому что если мы вдруг получим что-то в каком-то клиенте, который используем для отладки, то проще прочитать строку, чем потом дополнительно десервировать бинарник.
А поддержка перехватчиков в рантайм-библиотеках, то есть возможность встроить эти метаданные прозрачно для бизнес-логики через перехватчики, это очень удобно.
Для Java это ClientInterceptor и ServerInterceptor. Плюс в том, что у него более длинное название, поэтому я не буду его читать.
Единственный положительный момент, хочу отметить, что это пока экспериментальные фичи, но не думаю, что их когда-нибудь вырежут. Возможно, там немного изменится API. А дело в том, что перехватчики, помимо трассировки, конечно, используются для какой-то аутентификации.
То есть в gRPC есть встроенная аутентификация, но она подходит не всем; Я хочу что-то свое.
А без перехватчиков никак не обойтись, так что вряд ли его как-то вырезают, а, скорее всего, разовьют.
Еще я хотел отдельно поговорить о юнит-тестировании.
Существует довольно хорошая поддержка модульного тестирования.
Я написал примеры для Java. Можно сделать заглушки канала и сервера и на их основе сгенерировать сервис, то есть с юнит-тестированием все в порядке.
Больше ничего придумывать не нужно, уже есть готовое решение.
Типичный вопрос.
Похоже, что gRPC — это двоичный протокол.
HTTP/2 и вообще.
Можно ли как-то его отладить и разобраться, что там происходит? Тот же типичный ответ: да, можно.
Некоторые инструменты уже есть.
Прежде всего, в состав gRPC входит инструмент grpc_cli, аналог Curl. Он очень простой, но позволяет делать все с помощью звонков.
По моему мнению, он даже позволяет осуществлять потоковую передачу.
И что самое удобное, если вы установили gRPC на локальную машину, он у вас уже есть из коробки.
Есть более гламурные вещи, например, Эванс.
Попробовал, это такой интерактивный CLI: даёт подсказки, когда заполняешь сообщение протобаффа, сразу пишет какие поля.
В целом он более интерактивный.
Возможно для некоторых скриптов он не подойдет, но использовать можно, он неплох, я пробовал.
Если кому-то нужен пользовательский интерфейс — например, он привык к Postman — есть BloomRPC. Визуально он очень похож на Postman. Но Почтальон, конечно, круче; он развивается гораздо дольше.
Однако BloomRPC неплох, базовый функционал он выполняет. Это некоторые основные инструменты, которые я попробовал.
На практике я, конечно, в основном использую grpc_cli. Два других инструмента я попробовал при подготовке к докладу.
Но в целом их гораздо больше.
Рекомендую посмотреть и тем, кто хочет попробовать.
Далее у меня будет ссылка на страницу, где вы сможете увидеть список всех известных клиентов.
Возможно, кто-то найдет что-то лучше.
Если найдете что-то лучше, пишите.
Конечно, индустрия не стоит на месте, и альтернативы gRPC есть.
Об этих альтернативах хотелось бы поговорить подробнее.
Некоторые из них мы попробовали, некоторые я нашла в процессе приготовления.
Есть Сваггер.
Наверное, в случае с HTTP/1 это самая зрелая вещь.
Можно описать свой протокол в формате OpenAPI и таким же образом сгенерировать на его основе клиента.
Клиент также может быть создан для разных языков.
Кажется, если инфраструктура еще не готова к HTTP/2, то Swagger — лучшее решение.
Я считаю, что WSDL — это классика.
Он также используется.
Она очень похожа по своим идеям на Сваггера, но только не такая хайповая, не такая модная.
Но в целом он используется.
Я использовал его один раз.
Для тех, у кого, например, нет необходимости строить сервисы между разными языками, есть JAX-RS, который хорошо работает для Java. Его тоже можно использовать.
Отдельно хотелось бы отметить Twirp. Что это? Насколько я понимаю из документации, это вещь только для Go. Я никогда не использовал его.
Я знаю, что те, кто пишет на Go, перешли с gRPC на Twirp. В чем идея? Я сказал, что gRPC — это довольно абстрактная вещь, которая не определяет, например, какой IDL использовать.
Аналогично, в прото-формате не указано, что поверх него можно создавать только службы gRPC. Вы можете написать свой плагин для протокола, который будет генерировать сервисы на любом языке, на любых технологиях и обмениваться ими любым способом.
И Twirp как раз использует эту идею.
Они берут формат proto и на его основе генерируют сервисы, работающие по протоколу HTTP/1.1 и обменивающиеся как двоичными файлами, так и JSON. Эта идея не нова, просто Twirp выпустила ее с открытым исходным кодом для Go. У нас тоже была такая идея, и есть реализация плагина, который генерирует вот такие клиенты на базе Jetty для Java. Кажется, это работает нормально, но есть ограничения.
Теги: #api #Микросервисы #C++ #java #grpc #rest #rpc #команда Яндекс.
маркета #Яндекс.
маркет #удаленный вызов процедур
-
Маркетинговые Ходы Разработчиков Cms
19 Oct, 24 -
Rip-Строка
19 Oct, 24 -
Социальной Сети Comby.ru Исполнился Год
19 Oct, 24