Создание чата на GoLang с gRPC и Protocol buffer. Часть 1

Предлагаю в этой статье создать чат на языке GoLang, для отправки сообщений в реальном времени будем использовать gRPC и Protocol buffer. Что это и как работает – познакомимся в процессе создания. Мы напишем серверную и клиентскую часть, и если вы захотите – можете сделать доступным его через ngrok (или задеплоить на сервер) и общаться с вашими друзьями. К чему тонны этих мессенджеров, пусть все теперь используют наш новый сверх-технологичный быстрый и крутой чат. Поехали!

Что такое gRPC

Пожалуй первый вопрос который у вас возник – это что такое gRPC. Если для вас также не знакомо слово GoLang – то предлагаю для начала немного почитать документацию и пройти туториал по этому языку программирования.

Для начала давайте разберемся что такое RPC, а после уже будет разбираться с этой g. RPC – это аббревиатура Remote Procedure Call. Удаленный вызов процедур. Это класс технологий, который позволяет вызывать процедуры и функции в другом адресном пространстве, другими словами – удаленно вызывать функции.

gRPC расшифровывается аналогично, только здесь еще буква g добавлена (g – Google). Это независящий. от языка программирования фреймворк для удаленного вызова процедур разработанный компанией Google. Данная технология стара как мир, но активное начала использоваться последние время. Официальная документация https://grpc.io/. Но к ней предлагаю обратиться после того как прочитаете эту статью и ничего не поймете (или захотите узнать больше деталей).

Данный Фреймворк позволяет создавать клиентские библиотеки для работы с бекэндом на более чем 10 языках программирования. Высокая производительность достигается за счет использования протокола HTTP/2 и Protocol buffer, о котором поговорим вот уже скоро.

Типы RPC

Перейдем к типам RPC, то есть как клиент и сервис могут общаться друг с другом.

Unary RPC

Самый простой и понятный тип. Клиент отправляет один запрос и получает один ответ от сервера. Что может быть проще!

Server-side streaming RPC

Вот здесь уже есть отличие от обычного запроса-ответа. Клиент отправляет один запрос и читает в ответ стрим сообщений из сервера. К примеру, я запрашиваю список баров до которых можно дойти пешком и в ответ получаю стрим из сообщений по каждому бару. Да, это можно сделать отправив запрос и получив ответ, но кому оно нужно. Или же список баров, в которых есть где присесть. Отправили один запрос – и нам постоянно приходят новые, в которых появилось место. И это происходит в одном соединении. И это уже не реализуешь с помощью унарного типа.

Сlient-side streaming RPC

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

Bidirectional streaming RPC

И последний – микс стрима от клиента и сервера. Клиент отправляет стрим данных, бекенд обрабатывает их и отправляет свой стрим. Причем они могут обрабатываться по очереди ping-pong – то есть запрос-ответ, а могут и независимо друг от друга. К примеру, это может быть полезно для создания чата. Но мы будем использовать не этот тип. Увы.

Protocol buffers

Слишком много теории, но давайте еще рассмотрим что такое Protocol buffers и дальше обещаю перейти к примеру.

В REST API мы привыкли использовать JSON формат для передачи данных. Кто-то может пользуется XML, но мне лично он не так приятен. Для gRPC используется свой формат для стерилизации структурированных данных.

Protocol buffer – это гугловский языко- и платформо- независимый механизм для сериализации данных. Вы определяете структуру данных, необходимые методы и далее специально написанный генератор создает необходимый код на базе этих данных. На любых языках. Почти на любых.

Давайте взглянем на пример и тогда все станет куда яснее.

service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

Что мы здесь видим?

У нас есть сервис приветствия Greeter и метод SayHello. Функция, которая вызывается удаленно на сервере и возвращает данные клиенту, помечается как rpc. Это унарный тип запроса, мы отправляем на сервер один реквест HelloRequest и получаем один респонс HelloReply. Структуры данных, служащие для обмена информацией между взаимодействующими узлами, помечаются как message.

Теперь подробнее посмотрим на HelloRequest. Он содержит одно поле name. Из формата в целом понятно что это поле типа Строка. Так же у нас есть здесь цифра. Если бы у нас было еще одно поле (как будет дальше в примере создания консольного чата) – то мы бы поставили ему цифру два. Скорее всего.

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

Давайте еще глянем на такую функцию:

service Greeter {
  rpc SayBidiHello (stream HelloRequest) returns (stream HelloReply) {}
}

Как вы думаете, какое отличие у этой функции, кроме того что у нее другое название? Это пример двунаправленного стрима: как со стороны клиента, так и со стороны сервера.

Давайте назовем наш файл file.proto с кодом унарного метода и скомпилируем используя следующую команду:

protoc --go_out=. --go_opt=paths=source_relative \
    --go-grpc_out=. --go-grpc_opt=paths=source_relative \
    file.proto

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

Во второй части мы перейдем непосредственно к примеру.

Leave a Reply