Обзор gRPC для начинающих, использующих язык Go

RPC

RPC - это модель сетевого программирования или метод межпроцессного взаимодействия, используемый для двухточечной связи между программными приложениями.

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

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

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

Как это работает?

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

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

Вариант использования

Мы будем внедрять службу Gravatar для генерации URL-адресов, содержащих хэш MD5 связанного адреса электронной почты. Их можно использовать для загрузки глобально уникальных аватаров с веб-сервера Gravatar.

Наши клиенты смогут общаться с сервером по протоколу RPC, отправляя свои электронные письма и желаемый размер изображения. В ответ они получат персональную ссылку на свой аватар, настроенный на https://gravatar.com.

Буферы протокола

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

В отличие от XML или JSON, здесь вы сначала определяете схему в .proto файле. Это формат, подобный JSON, но более простой, меньший по размеру, строго типизированный, понятный только от клиента к серверу и более быстрый для Marshall / Unmarshall. Например:

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

Установка

Мы компилируем буфер протокола с помощью компилятора protoc, и целевой файл создается для языка программирования. Для Go компилятор генерирует .pb.go файл с типом для каждого типа сообщения в вашем файле.

Чтобы установить компилятор, запустите:

brew install protobuf

Затем создайте и инициализируйте новый проект внутри своего GOPATH:

mkdir profobuf-example
cd profobuf-example
go mod init

Затем установите поддержку Go для буферов протокола Google:

go get -u github.com/golang/protobuf/protoc-gen-go
go install github.com/golang/protobuf/protoc-gen-go

Наконец, скомпилируйте все .proto файлы:

protoc --go_out=. *.proto

Мой скомпилированный файл выглядит следующим образом:

gRPC

GRPC - это высокопроизводительная структура RPC, построенная с использованием буферов протокола (как в качестве языка определения интерфейса, так и в качестве основного формата обмена сообщениями) и HTTP / 2.

После того, как вы указали свои структуры данных, вы также определяете службы gRPC в обычных .proto файлах с параметрами метода RPC и типами возврата, указанными как сообщения буфера протокола. В нашем случае это именно так:

service GravatarService {
    rpc Generate(GravatarRequest) returns (GravatarResponse) {}
}

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

protoc --go_out=plugins=grpc:. *.proto

Разница выглядит следующим образом:

Реализация

Теперь мы сгенерировали серверный и клиентский код, поэтому нам нужно реализовать и вызвать эти методы в нашем приложении.

Начнем с реализации базовой логики нашего «основного бизнеса»:

В этом нет ничего особенного, это обычная генерация MD5 в GO.

Однако реализация сервера более интересна:

Мы определили порт для запуска сервера и структуру gravatarService, которая охватывает определение GravatarService из .proto файла. Как видите, мы также реализовали на нем требуемый метод Generate, который получает GravatarRequest и выдает соответствующий GravatarResponse.

Мы открываем tcp соединение на данном порту, создаем новый сервер gRPC, который регистрирует наш обработчик, и запускаем его на открытом слушателе. Теперь мы готовы обрабатывать запросы.

Реализация клиента тоже не намного сложнее. Я бы сказал еще проще:

Мы открываем конкретное соединение по данному адресу (в нашем случае это localhost с ранее определенным портом) и регистрируем нового клиента на данном соединении. Имейте в виду, что мы должны закрыть и соединение, и выключить context при выходе из нашей программы.

Наконец, мы вызываем метод Generate на нашем клиенте с GravatarRequest и нашими данными внутри него. Если это удастся, мы можем распечатать полученный URL с нашим хешем.

Подпишитесь, чтобы сразу получать самые свежие материалы
https://tinyletter.com/KamilLelonek

Резюме

Буферы протокола предлагают очень реальные преимущества с точки зрения скорости кодирования и декодирования, размера данных в сети и т. Д. Теперь вы можете задаться вопросом, каковы преимущества gRPC по сравнению с обычным JSON REST API. Давайте рассмотрим пару вещей:

Схемы

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

Обратная совместимость

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

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

Эволюция схемы

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

Валидации

Ключевые слова required, optional и repeated в определениях протокольных буферов чрезвычайно эффективны. Они позволяют кодировать на уровне схемы форму вашей структуры данных, а детали реализации того, как классы работают на каждом языке, обрабатываются за вас. Библиотеки вызовут исключения, например, если вы попытаетесь закодировать экземпляр объекта, в котором не заполнены обязательные поля. Вы также всегда можете изменить поле с required на optional или наоборот, просто перейдя к новому нумерованному полю. за это значение. Такая гибкость, закодированная в семантике формата сериализации, является невероятно мощной.

Языковая совместимость

Поскольку буферы протоколов реализованы на разных языках, они значительно упрощают взаимодействие между многоязычными приложениями в вашей архитектуре. Если вы представляете новую службу в NodeJS, Go или даже в Elixir, вам просто нужно передать файл proto генератору кода, написанному на целевом языке, и у вас есть хорошие гарантии безопасности и взаимодействия между этими архитектурами.

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

Большое спасибо за



который вдохновил меня на написание этой статьи и сделал обзор кода.