Ну привет! Готов узнать что такое gRPC, как работает и для чего нужно?

Что такое gRPC и Protobuf?
Ты скажешь: “Я и так знаю! Это та штука, с помощью которой я выходил в сеть Интернет со своего Siemens CX65, чтобы скачать новые полифонические трэки!”
Нет, дружище, то был GPRS… уххх… как же олдскулы то свело…

Кароче, gRPC это такая система, или фреймворк, который расшифровывается как - Remote Procedure Calls. Ты, наверное, подумал: “Падажии, а g что значит?”
Нууу, кто-то говорит что Google, потому что это их разработка, а официальная документация говорит что g значит gRPC, то есть да это “gRPC Remote Procedure Calls”.
А сами разработчики дико рофлят и каждую новую версию меняют название, так что это особо ни на что не влияет.

Нам в мерионе больше всего нравится - glamorous.
А вот со второй частью названия давай разберемся. Remote Procedure Calls, что переводится как удаленный вызов процедур. Очень интересная технология, которая позволяет одному серверу инициировать выполнение какой-либо функции, которая есть на другом сервере, как если бы она была на первом.

Так это же вроде обычный REST API, не?
Тоже делаем запрос на сервер, что-то там выполняется, и мы получаем ответ.

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

Вот эти POST, GET, DELETE, PUT, разные пути в URL, параметры в строке запроса и прочее.

Поэтому говорят, что он ориентирован на ресурсы - типа получить страницу, отправить форму, вот это вот все.
У RPC же другое назначение - вызов функций. Когда ты делаешь это на своем сервере, то обычно просто передаешь туда одни переменные и получаешь ответ, который кладешь в другую переменную.

Ничего лишнего, ни метода запроса (например GET, POST, PUT), ни путей (например /product).
И с RPC также, только вызываемая функция далеко-далеко на другом сервере!

До gRPC одним из популярных вариантов реализации RPC был JSON-RPC. Давай ка посмотрим на пример с ним чтобы осознать разницу.
Вот так у нас выглядит простой REST запрос на обновление цены на товар в интернет-магазине:

Слишком… бэйсик, да?
А вот так с JSON-RPC! Тут мы шлем запрос на базовый эндпоинт (post /products), указываем метод (method), то есть функцию которую там вызвать, и в блоке params передаем входные параметры функции:

Неплохо, выглядит удобно для вызова функции на удаленном сервере, но когда в здание ворвался gRPC все стало действительно интересно.
Так что же сделали в гугле? Ну для начала, они решили избавиться от формата JSON…

Который используется в REST, и пересели на формтат Protocol Buffers, он же - Protobuf. Что это за зверь такой?
Protobuf - это бинарный формат, то есть если в json мы передавали просто текст в виде пар ключ-значение, то в протобафе мы переводим текст, в двоичный вид с ноликами и единичками, что позволяет нам сжимать сообщения, и соответственно увеличивать скорость передачи данных. (замазать мат на картинке)

Конечно, теперь надо кодировать сообщение перед отправкой, и декодировать на приеме, или как еще это называется, проводить сериализацию и десиреализацию, но зато передача данных по сети получается быстрее.
Также протобаф в отличие от json’а имеет стогую типизацию. То есть в json’е мы могли написать что-то вроде такого:

И что ты мне сделаешь? Я в другом городе!
А в протобафе такой фокус не пройдёт. Мы должны заранее определить что у нас будет в сообщении, каких типов будут поля, являются ли они обязательными и прочее. Зато, тут мы можем избавить себя от валидации данных когда будем их принимать, потому что точно знаем что должно прийти.
Теперь посмотрим как это работает. Для начала мы создаем файл .proto, и описываем сообщения, которые будут передаваться.
Тут в примере мы создаем сообщение Person в котором будут передаваться несколько полей. Сначала мы пишем тип поля, будет это строка, число, время или что-то еще (string и int32). Далее пишем название поля (name, id, email), равно и его уникальный номер, который понадобится потом для кодирования сообщения. Еще можно сказать обязательное ли это поле или опциональное, указа перед нем required или optional.

Чтобы описать это сообщение мы использовали так называемый язык описания интерфейсов он же IDL (Interface Description Language). Это по сути набор правил, указывающий как описывать сообщения и что можно там использовать. А использовать тут можно огого сколько всего.
И кучу различных типов, и енАмы, и массивы (repeated) и мапы (map), и встраивать в одни сообщения другие, как X-Zibit завещал (обрати внимание, что в SearchResponse поле Result, это сообщение Result сверху), все что пожелаешь.

На этом этапе уже можно запустить компилятор, который сгенерирует нам код с функциями для создания этих сообщений, их кодирования, декодирования.
Кстати, протобаф можно использовать и без gRPC, если есть такая потребность, ну например хранить в таком виде файлы.
Но мы всё таки тут собрались ради gRPC, поэтому нам надо дописать пару строчек в наш протофайл. Нужно создать блок service, где указывается название сервиса (SearchService), затем название метода (Search), какое сообщение он принимает (SearchRequest) и какое возвращает (SearchResponse).

Используя этот сгенерированный код из файлов, мы создаем специальные сервисы на двух сторонах.
Со одной стороны мы запилим так называемый gRPC Server, который будет слушать выделенный сетевой порт, принимать и обрабатывать входящие запросы, декодировать сообщения, вызывать ту функцию, что просит клиент, и затем кодировать и отправлять ответ. На другой стороне, откуда мы будем делать запросы, мы создаем так называемый gRPC Stub, сервис который будет создавать сообщения, отправлять их на указанный обработчик сервера и получать ответ.
Когда нам нужно вызвать функцию на другом сервере, мы идем в наш gRPC стаб, собираем там сообщение, вызываем нужную функцию стаба которая отправит сетевой запрос на другой сервер, а там gRPC сервер поймает этот запрос, вызовет че мы там хотели, и вернет запрос тем же путем (на рисунке можно показать как стрелки меняются на противоположные в момент ответа)

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

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

Также в HTTP2 используется мультиплексирование запросов и ответов, а это значит что для каждого нового запроса не нужно создавать новое соединение как в первой версии. Снова профит!
Думали всё? Ан нет! Как вам улучшенная работа с заголовками? В запросах теперь могут отправляться только те заголовки, которые поменялись относительно предыдущего запроса.

И все это дает нам невиданную скорость! Вжжжжжжух!

И это еще не все! Закажите HTTP2 прямо сейчас и получите возможность потоковой передачи данных и работы в полнодуплексном режиме, которая отлично подойдёт для создания стримов с gRPC!
То есть можно передавать не одно, а целый поток сообщений, что создает невероятное удобство при отправке большого количества данных за раз!
В обычном случае (Unary), после каждого запроса клиент ждет ответ от сервера. А в случае стриминга у нас есть несколько вариантов. Есть стриминг со стороны клиента, (Server streaming), когда в ответ на запрос от клиента сервер шлет поток сообщений и в конце сигнализирует что стрим закончен, всем спасибо, все свободны подписывайтесь на канал.. на наш.
А можно и наоборот (Client streaming) когда клиент шлет поток сообщений, а сервер принимает их и затем отвечает подтверждающим сообщением что все принял-понял, можешь бежать на переменку.

А еще можно одновременно независимо друг от друга слать в обе стороны (Bidirectional streaming).
Ну, а gRPC во всех этих случаях гарантирует порядок сообщений в рамках отдельного вызова, который можно отменить, если вдруг на той стороне все зависло.
И все вот это вот, отлично подходит для микросервисной архитектуры. Обязательно зацени наше полное видео про микросервисы, но быстро напомним, что это когда мы разбиваем одно большое приложение на кучу маленьких и независимых. И вот для их общения друг с другом отлично подходит gRPC - это быстро, просто, гибко, надежно и молодежно!
А если ты хочешь стать крутым разработчиком который умеет создавать микросервисы, знает как работает язык программирования под капотом и умеет пользоваться самыми разными фреймворками, то записывайся на бесплатный вводный урок продвинутого курса по Python где Сурен Хоренян, который, кстати, является техлидом в MTS AI, поделится с тобой потоком знаний обо всем этом и поможет прокачать твои навыки разработки. А да… всех, кто пройдёт вводный урок ждёт скидка на полный курс.
Микросервисы микросервисами, но если очень хочется, то можно делать запросы даже с клиентов. Например, есть набор библиотек gRPC-Web, который позволяет делать обычные одиночные запросы с веб-браузера, а так же получать стрим со стороны сервера.
Вот такая вот классная штука, этот ваш gRPC!