gRPC: запуск и эксплуатация облачных приложений. Go и Java для Docker и Kubernetes
Қосымшада ыңғайлырақҚосымшаны жүктеуге арналған QRRuStore · Samsung Galaxy Store
Huawei AppGallery · Xiaomi GetApps

автордың кітабын онлайн тегін оқу  gRPC: запуск и эксплуатация облачных приложений. Go и Java для Docker и Kubernetes

 

Касун Индрасири, Данеш Курупу
gRPC: запуск и эксплуатация облачных приложений. Go и Java для Docker и Kubernetes
2021

Переводчики О. Сивченко, С. Черников

Технический редактор Н. Хлебина

Литературный редактор Н. Хлебина

Художники В. Мостипан, Г. Синякина (Маклакова)

Корректоры Н. Гринчик, Е. Павлович

Верстка Г. Блинов


 

Касун Индрасири, Данеш Курупу

gRPC: запуск и эксплуатация облачных приложений. Go и Java для Docker и Kubernetes. — СПб.: Питер, 2021.

 

ISBN 978-5-4461-1737-6

© ООО Издательство "Питер", 2021

 

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

 

Введение

В наши дни приложения часто «общаются» между собой по компьютерным сетям, используя технологии межпроцессного взаимодействия. gRPC — это разновидность межпроцессного взаимодействия, основанная на высокопроизводительных удаленных вызовах процедур (remote procedure calls, RPC) и предназначенная для создания распределенных приложений и микросервисов. Благодаря появлению микросервисов и облачно-ориентированных приложений популярность gRPC растет экспоненциально.

Зачем мы написали эту книгу

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

Чтобы исправить ситуацию, мы написали эту книгу. Она поможет вам разобраться в основах gRPC, понять отличия данного протокола от традиционных средств межпроцессного взаимодействия, изучить практические методы коммуникации по gRPC и научиться создавать gRPC-приложения на Go и Java. Здесь вы узнаете, как устроена эта технология, как развертывать gRPC-приложения в промышленных условиях и как gRPC интегрируется в Kubernetes и облачную экосистему в целом.

Целевая аудитория

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

Мы исходим из того, что разработчики и архитекторы, читающие данную книгу, имеют общее представление о распределенных вычислениях, включая методы межпроцессного взаимодействия, сервис-ориентированную архитектуру (service-oriented architecture, SOA) и микросервисы.

Структура издания

Эта книга написана так, что теоретический материал объясняется на примерах из реальной жизни. Мы активно используем демонстрационный код на Go и Java, чтобы читатель мог сразу получить практический опыт применения концепций, с которыми знакомится. Мы разделили эту книгу на восемь глав.

• В главе 1 вы получите общее представление об основах gRPC и сможете сравнить эту технологию с аналогичными разновидностями межпроцессного взаимодействия, такими как REST, GraphQL и прочими механизмами RPC.

• На страницах главы 2 вы получите свой первый практический опыт создания полноценного gRPC-приложения на Go или Java.

• В главе 3 вы исследуете методы работы с gRPC на реальных примерах.

• Если вы уже имеете опыт использования gRPC и хотите узнать, как устроена эта технология, то глава 4 для вас. Здесь мы пройдемся по каждому этапу взаимодействия сервера и клиента и покажем, как gRPC работает на уровне сети.

• В главе 5 вы изучите некоторые из самых важных расширенных возможностей gRPC, такие как использование перехватчиков, работа с метаданными, мультиплексирование, балансировка нагрузки и т.д.

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

• В главе 7 мы пройдемся по всему циклу разработки gRPC-приложений. Будут рассмотрены тестирование, интеграция с CI/CD, развертывание и запуск в Docker и Kubernetes, а также мониторинг.

• В главе 8 мы обсудим некоторые полезные вспомогательные компоненты, разработанные с расчетом на gRPC. Большинство из этих проектов подходит для создания реальных gRPC-приложений.

Использование примеров кода

Все примеры кода и дополнительные материалы доступны для скачивания по адресу grpc-up-and-running.github.io. Мы настоятельно рекомендуем использовать код, представленный в данном репозитории, по ходу чтения. Это позволит вам лучше понять концепции, с которыми вы знакомитесь.

Эти примеры кода поддерживаются и обновляются с учетом последних версий библиотек, зависимостей и инструментов разработки. Иногда содержимое репозитория может немного отличаться от кода, приведенного в книге. Если вы заметили некие расхождения (как отрицательные, так и положительные) в наших листингах, то мы очень просим вас оформить запрос на включение изменений (pull request, PR).

Вы можете использовать представленные здесь примеры кода в собственных программах и документации. Если вы не воспроизводите существенную часть кода, то связываться с нами не нужно. Это, например, касается ситуаций, когда вы включаете в свою программу несколько фрагментов кода, которые приводятся в данной книге. Однако на продажу или распространение примеров из книг издательства O’Reilly требуется отдельное разрешение. Цитировать книгу и приводить примеры кода при ответе на вопрос вы можете свободно. Но если хотите включить существенную часть приведенного здесь кода в документацию своего продукта, то вам следует связаться с нами.

Мы приветствуем, но не требуем отсылки на оригинал. Отсылка обычно состоит из названия, имени автора, издательства, ISBN и копирайта. Например: «gRPC: запуск и эксплуатация облачных приложений. Go и Java для Docker и Kubernetes», Касун Индрасири и Данеш Куруппу (Питер). Copyright 2020 Kasun Indrasiri and Danesh Kuruppu, 978-5-4461-1737-6.

Если вам кажется, что ваше обращение с примерами кода выходит за рамки добросовестного использования или условий, перечисленных выше, то можете обратиться к нам по адресу permissions@oreilly.com.

Условные обозначения

В этой книге используются следующие условные обозначения.

Курсив

Курсивом выделены новые термины.

Моноширинный шрифт

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

Моноширинный жирный шрифт

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

Моноширинный курсивный шрифт

Показывает текст, который должен быть заменен значениями, введенными пользователем, или значениями, определяемыми контекстом.

Шрифт без засечек

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

 

Этот рисунок указывает на совет или предложение.

Такой рисунок указывает на общее замечание.

Этот рисунок указывает на предупреждение.

Благодарности

Выражаем признательность научным редакторам этой книги, Жюльену Андрю, Тиму Реймонду и Райану Микеле. Кроме того, мы хотели бы поблагодарить старшего редактора Мелиссу Поттер за ее советы и поддержку, а также нашего редактора Райана Шоу за всю оказанную им помощь. И конечно же, мы говорим спасибо всему сообществу gRPC, которое создало этот замечательный проект с открытым исходным кодом.

От издательства

Ваши замечания, предложения, вопросы отправляйте по адресу comp@piter.com (издательство «Питер», компьютерная редакция).

Мы будем рады узнать ваше мнение!

На веб-сайте издательства www.piter.com вы найдете подробную информацию о наших книгах.

Глава 1. Введение в gRPC

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

Микросервисная архитектура

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

С появлением микросервисной (oreil.ly/q6N1P) и облачно-ориентированной (oreil.ly/8Ow2T) архитектуры традиционные приложения, обладающие разными бизнес-возможностями, подверглись разделению на более мелкие, автономные и бизнес-ориентированные сущности, известные как микросервисы. И эти микросервисы должны связываться друг с другом по сети, используя методики межпроцессного (межсервисного, межпрограммного) взаимодействия (inter-process communication, IPC). Если бы интернет-магазин из предыдущего примера был реализован с помощью микросервисной архитектуры, то состоял бы из нескольких взаимосвязанных микросервисов, которые бы отвечали за управление, поиск, оформление покупки, доставку и т.д. По сравнению с работой традиционных приложений данный подход требует большего количества сетевых соединений, поскольку задействовано множество мелких компонентов. Поэтому, какой бы архитектурный стиль вы ни выбрали (традиционный или на основе микросервисов), методы межпроцессного взаимодействия — один из важнейших аспектов современных распределенных приложений.

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

Что касается реализации синхронного взаимодействия в виде запросов и ответов, в современных облачно-ориентированных приложениях и микросервисах для этого чаще всего используют уже устоявшийся архитектурный стиль REST. Ваше приложение или сервис состоит из набора ресурсов, для чтения и модификации которых применяются сетевые вызовы, отправляемые по протоколу HTTP. Однако в большинстве случаев сервисы REST довольно громоздки, неэффективны и подвержены ошибкам в ходе построения межпроцессного взаимодействия. Часто возникает необходимость в хорошо масштабируемой технологии IPC со слабой связанностью, которая была бы более эффективной по сравнению с REST. Именно эту роль берет на себя gRPC — современный стиль межпроцессного взаимодействия для создания распределенных приложений и микросервисов (мы сравним gRPC и REST позже в данной главе). gRPC в основном использует синхронные запросы и ответы, но вместе с тем имеет полностью асинхронный или потоковый режим, доступный после установления соединения.

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

Что такое gRPC

gRPC (значение буквы g меняется с каждой новой версией (oreil.ly/IKCi3)) — технология межпроцессного взаимодействия, позволяющая соединять, вызывать, администрировать и отлаживать распределенные гетерогенные приложения в стиле, который по своей простоте мало чем отличается от вызова локальных функций.

При разработке gRPC-приложения первым делом нужно определить интерфейс сервисов. Данное определение содержит информацию о том, как потребители могут обращаться к вашим сервисам, какие методы можно вызывать удаленно, какие параметры и форматы сообщений следует применять при вызове этих методов и т.д. Язык, который используется в спе­цификации сервиса, называется языком описания интерфейсов (interface definition language, IDL).

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

Понять основополагающие концепции gRPC попробуем на реальном примере использования микросервисов, основанных на этой технологии. Допустим, мы занимаемся разработкой приложения для розничной торговли, состоящего из нескольких микросервисов, и хотим, чтобы один из них возвращал описание товаров, доступных для продажи, как показано на рис. 1.1 (в главе 2 мы реализуем данный сценарий с нуля). Сервис ProductInfo спроектирован так, чтобы к нему был доступ в сети по протоколу gRPC.

Определение сервиса находится в файле ProductInfo.proto, который используется для генерации кода как на серверной, так и на клиентской стороне. В этом примере мы исходим из того, что сервис написан на языке Go, а потребитель — на Java. Сетевое взаимодействие обоих объектов происходит по HTTP/2.

 

Рис. 1.1. Микросервис и потребитель на основе gRPC

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

Определение сервиса

В качестве IDL для определения интерфейса сервисов gRPC использует Protocol Buffers (https://oreil.ly/iFi-b) — расширяемый механизм сериализации структурированных данных, который не зависит от языка и платформы (мы подробно рассмотрим его в главе 4, а пока вам достаточно знать, что это средство сериализации). Определение интерфейса сервиса хранится в proto-файле — обычном текстовом файле с расширением .proto. gRPC-сервисы описываются в стандартном формате Protocol Buffers; при этом параметры методов RPC и типы возвращаемых значений указываются в виде сообщений. Поскольку определение сервисов — расширение спецификации Protocol Buffers, генерация кода из вашего proto-файла возможна при наличии специального дополнения gRPC.

В нашем демонстрационном примере интерфейс сервиса ProductInfo в формате Protocol Buffers выглядит так, как показано в листинге 1.1. Определение сервиса состоит из определения интерфейса, в котором указаны удаленные методы, их входящие и исходящие параметры, а также определения типов (или форматы сообщений) этих параметров.

Листинг 1.1. Определение gRPC-сервиса ProductInfo с помощью Protocol Buffers

// ProductInfo.proto

syntax = "proto3";

package ecommerce;

 

service ProductInfo {

    rpc addProduct(Product) returns (ProductID);

    rpc getProduct(ProductID) returns (Product);

}

 

message Product {

    string id = 1;

    string name = 2;

    string description = 3;

}

 

message ProductID {

    string value = 1;

}

Определение сервиса начинается с указания версии Protocol Buffers (proto3), которую мы используем.

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

Определение интерфейса gRPC-сервиса.

Удаленный метод для добавления товара, который возвращает ID этого товара в качестве ответа.

Удаленный метод для получения товара по его ID.

Определение формата/типа сообщений Product.

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

Пользовательский тип для идентификационного номера товара.

Таким образом, сервис — это набор методов (например, addProduct и getProduct), которые можно вызывать удаленно. У каждого метода есть тип возвращаемых значений, либо определяющийся в рамках самого сервиса, либо импортирующийся в определение Protocol Buffers.

Входящие и возвращаемые параметры могут иметь пользовательские типы (например, Product и ProductID) или стандартные типы Protocol Buffers (https://oreil.ly/0Uc3A), описанные в определении сервиса. Тип состоит из сообщений, каждое из которых представляет собой небольшой логический блок информации с набором пар «имя — значение» (мы называем их полями). Каждое поле имеет уникальный номер (например, stringid=1), с помощью которого оно идентифицируется в двоичном формате сообщений.

На основе этого определения сервиса генерируется серверная и клиентская части вашего gRPC-приложения. В следующем подразделе мы подробно поговорим о реализации gRPC-сервера.

gRPC-сервер

Созданное вами определение сервиса можно использовать для генерации серверного или клиентского кода (а также стандартного кода Protocol Buffers). Для этого в Protocol Buffers предусмотрен компилятор protoc.

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

1. Реализовать логику сервиса на основе сгенерированного каркаса, переопределив его базовый класс.

2. Запустить gRPC-сервер, чтобы принимать запросы от клиентов и возвращать ответы сервиса.

При реализации логики сервиса первым делом нужно сгенерировать его каркас на основе его же определения. Например, в листинге 1.2 можно видеть удаленные функции, сгенерированные для сервиса ProductInfo на языке Go. В теле каждой из этих удаленных функций можно реализовать ее логику.

Листинг 1.2. Серверная реализация сервиса ProductInfo на языке Go

import (

  ...

  "context"

  pb "github.com/grpc-up-and-running/samples/ch02/productinfo/go/proto"

  "google.golang.org/grpc"

  ...

)

 

// реализация ProductInfo на Go

 

// удаленный метод для добавления товара

func (s *server) AddProduct(ctx context.Context, in *pb.Product) (

      *pb.ProductID, error) {

  // бизнес-логика

}

 

// удаленный метод для получения товара

func (s *server) GetProduct(ctx context.Context, in *pb.ProductID) (

      *pb.Product, error) {

  // бизнес-логика

}

Закончив с реализацией сервиса, вы должны запустить gRPC-сервер, который будет прослушивать клиентские запросы, перенаправлять их сервису и возвращать его ответы клиенту. Фрагмент кода в листинге 1.3 демонстрирует реализацию gRPC-сервера на Go для работы с сервисом ProductInfo.  Здесь мы открываем TCP-порт, запускаем gRPC-сервер и регистрируем на нем сервис ProductInfo.

Листинг 1.3. Запуск gRPC-сервера для сервиса ProductInfo, написанного на Go

func main() {

  lis, _ := net.Listen(„tcp", port)

  s := grpc.NewServer()

  pb.RegisterProductInfoServer(s, &server{})

  if err := s.Serve(lis); err != nil {

    log.Fatalf(„failed to serve: %v", err)

  }

}

Это все, что необходимо сделать на серверной стороне. Теперь перейдем к реализации клиента gRPC.

gRPC-клиент

С помощью определения сервиса можно сгенерировать не только серверный каркас, но и клиентскую заглушку. Она предоставляет те же методы, что и сервер, но при этом позволяет вызывать клиентский код; далее методы транслируются в удаленные (сетевые) вызовы функций, направленные к серверной стороне. Поскольку определения gRPC-сервиса не зависят от языка, вы можете сгенерировать клиентский и серверный код для любой поддерживаемой среды выполнения (с помощью сторонних реализаций (oreil.ly/psi72)) на ваш выбор. В нашем случае мы генерируем для сервиса ProductInfo заглушку на Java и серверный каркас на Go. Клиентский код представлен в листинге 1.4. Независимо от выбранного нами языка, реализация клиентской стороны состоит из установления соединения с удаленным сервером, подключения клиентской заглушки к этому соединению и вызова с ее помощью удаленных методов.

Листинг 1.4. gRPC-клиент для удаленного вызова методов сервиса

// создаем канал, используя адрес удаленного сервера

ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 8080)

   .usePlaintext(true)

   .build();

 

// инициализируем на основе этого канала блокирующую заглушку

ProductInfoGrpc.ProductInfoBlockingStub stub =

       ProductInfoGrpc.newBlockingStub(channel);

 

// вызываем удаленный метод с помощью блокирующей заглушки

StringValue productID = stub.addProduct(

       Product.newBuilder()

       .setName("Apple iPhone 11")

       .setDescription("Meet Apple iPhone 11." +

            "All-new dual-camera system with " +

            "Ultra Wide and Night mode.")

       .build());

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

Обмен сообщениями между клиентом и сервером

Когда gRPC-клиент обращается к gRPC-сервису, его gRPC-библиотека выполняет удаленный вызов процедуры по HTTP/2, используя Protocol Buffers и маршалинг. Серверная сторона распаковывает полученный запрос и вызывает соответствующую процедуру с помощью Protocol Buffers. Затем аналогичным образом сервер возвращает ответ клиенту. Для передачи данных gRPC задействует HTTP/2 — высокопроизводительный двоичный протокол обмена сообщениями с поддержкой двунаправленности. В главе 4 мы обсудим низкоуровневые аспекты передачи сообщений между клиентами и серверами, а также Protocol Buffers и то, как gRPC использует HTTP/2.

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

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

Эволюция межпроцессного взаимодействия

С момента появления методы межпроцессного взаимодействия претерпели радикальные изменения. Мы можем наблюдать возникновение различных методик, направленных на удовлетворение современных нужд и предоставление лучших и более эффективных подходов к разработке. Поэтому важно, чтобы вы хорошо ориентировались в истории развития технологий IPC и понимали, как они в итоге эволюционировали в gRPC. Рассмотрим некоторые из наиболее популярных методик и сравним их с gRPC.

Традиционные подходы к RPC

Технология RPC была популярной разновидностью межпроцессного взаи­модействия для создания клиент-серверных приложений. Благодаря ей удаленный вызов функций или методов ничем не отличался от локального. Среди ранних реализаций RPC особой популярностью пользовались CORBA (Common Object Request Broker Architecture — общая архитектура брокера объектных запросов) и Java RMI (Remote Method Invocation — удаленный вызов методов); они использовались для создания и соединения сервисов и приложений. Однако большинство таких традиционных реализаций RPC страдали от излишней сложности, поскольку были построены поверх коммуникационных протоколов, таких как TCP, имеющих раздутые специ­фикации, которые затрудняли взаимодействие.

SOAP

Ввиду ограничений, присущих традиционным реализациям RPC наподобие CORBA, крупные корпорации, такие как Microsoft и IBM, разработали и начали активно продвигать SOAP (Simple Object Access Protocol — простой протокол доступа к объектам). Это стандартный подход к взаимодействию в сервис-ориентированной архитектуре (service-oriented architecture, SOA), который позволял сервисам (в контексте SOA их обычно называют веб-сервисами) обмениваться структурированными данными в формате XML; в качестве транспортного протокола чаще всего использовался HTTP.

...