В поддержку моего нового курса по архитектуре информационных систем Дизайн API, сегодня я покажу принципы и последовательность проектирования gRPC-сервиса на примере системы работы с поставщиками.
Еще раз о gRPC: краткий ликбез
В последнее время gRPC как технология интеграции информационных систем становится все более популярной. Этот фреймворк и протокол удалённого вызова процедур (Remote Procedure Calls) предложены компанией Google в 2015 году. Одним из главных достоинств gRPC считается скорость, которая обеспечивается благодаря следующим факторам:
- быстрый протокол HTTP/2 в качестве транспорта данных, который обеспечивает мультиплексирование, более эффективное сжатие заголовков запросов и ответов, а также двунаправленную потоковую передачу;
- бинарный формат сериализации полезной нагрузки Protocol Buffers (protobuf) со встроенной схемой данных.
С точки зрения разработчика gRPC хорош тем, что поддерживает разные языки программирования, позволяя, например, писать frontend на одной языке, а backend – на другом. Жесткая схема входных данных как параметров функций (процедур), вызываемых на удаленном сервере, а также результатов их обработки, обеспечивает валидацию сообщений и сокращает количество ошибок, связанных с несовпадением типов данных.
С точки зрения клиент-серверной архитектуры, gRPC поддерживает 4 варианта удаленного взаимодействия:
- унарное в стиле запрос-ответ, когда клиент отправляет серверу один запрос и получает один ответ;
- потоковое серверное, когда в ответ на запрос клиента сервер начинает потоковую передачу данных (стриминг);
- потоковое клиентское, когда клиент отправляет на сервер поток запросов и получает один ответ;
- двунаправленное потоковое, когда клиент и сервер передают друг другу потоки данных в обоих направлениях.
Такое разнообразие вариантов взаимодействия позволяет охватить множество интеграционных сценариев с разным характером передачи данных. Поэтому gRPC хорошо подходит для создания высокопроизводительных и масштабируемых распределенных систем, включая микросервисную и событийную архитектуры.
Основы архитектуры и интеграции информационных систем
Код курса
OAIS
Ближайшая дата курса
20 января, 2025
Продолжительность
16 ак.часов
Стоимость обучения
36 000 руб.
Проектирование gRPC-сервиса
Вспомнив, что такое gRPC, рассмотрим последовательность проектирования gRPC-сервиса. Как обычно, пойдем от требований. В качестве примера возьмем систему работы с поставщиками, которая должна иметь следующие функции:
- выдавать информацию о поставщике по его ИНН: название, адрес, телефон, email;
- принимать поток заказов этому поставщику до тех пор, пока количество единиц товара не превысит определенный лимит, например, 50 единиц. По завершении приема заказов система выдает общую сумму и суммарное количество товарных единиц, отправленных в заказ, а также дату и время окончания приема.
Сформулируем эти требования в виде вариантов использования (ВИ) и покажем на UML-диаграмме Use Case.
Скрипт PlantUML для этой UML-диаграммы Use Case:
@startuml left to right direction actor Пользователь as U rectangle gRPC-сервис { usecase "Узнать подробности о поставщике по ИНН" as UC1 usecase "Отправить заказы поставщику" as UC2 } U --> UC1 U --> UC2 @enduml
Чтобы понять характер каждого варианта использования, раскроем детали его выполнения на UML-диаграммах последовательности.
Скрипт PlantUML для этой UML-диаграммы последовательности:
@startuml title UC-1. Узнать подробности о поставщике по ИНН actor Пользователь as U participant "gRPC-сервис" as S U -> S : GetInfoByINN(ProviderINN) S --> U : ProviderData @enduml
Скрипт PlantUML для этой UML-диаграммы последовательности:
@startuml title UC-2. Отправить заказы поставщику actor Пользователь as U participant "gRPC-сервис" as S loop пока суммарное количество товарных единиц менее 50 U -> S : StreamOrders(OrderRequest) S --> U : OrderResponse end loop @enduml
Определим для каждого варианта использования полезную нагрузку запросов и ответов в формате protobuf.
Use Case | Запрос | Ответ | |||
№ | ВИ | Характер | Полезная нагрузка | Характер | Полезная нагрузка |
UC-1 | Узнать подробности о поставщике по ИНН | Пакетный (один запрос) | message ProviderINN { string inn = 1; } | Пакетный (один ответ на один запрос) | message ProviderData { string inn = 1; string name = 2; string phone = 3; string email = 4; string address = 5; } |
UC-2 | Отправить заказы поставщику | Потоковый («бесконечная» отправка запросов) | message OrderItem { string product = 1; int32 quantity = 2; double price = 3; } message OrderRequest { ProviderINN provider = 1; repeated OrderItem item = 2; } | Пакетный (один ответ на поток запросов) | message OrderResponse { int32 quantity = 1; double amount = 2; google.protobuf.Timestamp orderdate = 3; } |
В полезной нагрузке запросов и ответов, помимо перечисления полей и их типов данных также приведены значения для каждого поля в виде целых числе, начиная с 1. Этот уникальный номер называется тегом и нужен для идентификации поля при сериализации и десериализации данных. Теги начинаются с 1 и могут достигать 536870911, кроме диапазона от 19000 до 19999, который зарезервирован для использования в будущем и не должен использоваться в пользовательских сообщениях. Эти теги позволяют protobuf эффективно кодировать данные, обеспечивая компактное представление сообщений и быстродействие при их обработке. При сериализации данных, т.е. переводе их в бинарный вид из нулей и единиц, сохраняются не имена полей, а их уникальные номера. Это делает процесс передачи данных более эффективным по сравнению с текстовыми форматами, такими как JSON или XML.
После первичного определения gRPC-сервиса в таблице можно составить proto-файл, в котором будут описаны не только входные/выходные сообщения для RPC-функций в формате protobuf, но и сами функции. Сервис определяется с помощью ключевого слова service, после которого идет его имя. В теле сервиса перечисляются функции, определенные с помощью ключевого слова rpc с указанием входных параметров и возвращаемых результатов для каждой функции.
Для моего примера этот proto-файл выглядит так:
syntax = "proto3"; import "google/protobuf/timestamp.proto"; //для работы с датой и временем package order; // ИНН поставщика message ProviderINN { string inn = 1; // ИНН поставщика } // Информация о поставщике message ProviderData { string inn = 1; // ИНН поставщика string name = 2; // Имя поставщика string phone = 3; // Телефон поставщика string email = 4; // Электронная почта поставщика string address = 5; // Адрес поставщика } // Информация о товаре в заказе message OrderItem { string product = 1; int32 quantity = 2; double price = 3; } // Запрос на заказ message OrderRequest { ProviderINN provider = 1; // Информация о поставщика repeated OrderItem item = 2; // Список товаров в заказе } // Унарный ответ на поток заказов message OrderResponse { int32 quantity = 1; double amount = 2; google.protobuf.Timestamp orderdate = 3; } // Сервис service OrderService { rpc GetInfoByINN (ProviderINN) returns (ProviderData); rpc StreamOrders (stream OrderRequest) returns (OrderResponse); }
По своей сути, этот proto-файл является интерфейсом, который должны реализовать компоненты проектируемой системы, т.е. ее клиентская и серверная части. Заготовки для их фактической реализации на любом языке программирования генерируются автоматически из proto-файла с помощью protobuf-компилятора protoc. Сгенерированный код код включает в себя классы для сообщений, а также базовые классы и интерфейсы для реализации серверной логики и клиентских вызовов. Как это сделать, я расскажу в следующей статье, реализовав серверную и клиентскую часть рассмотренного gRPC-сервиса на Python.
Дизайн API — проектирование веб-приложений
Код курса
DAPI
Ближайшая дата курса
27 января, 2025
Продолжительность
16 ак.часов
Стоимость обучения
36 000 руб.
Больше примеров и подробностей про архитектуру и интеграцию информационных систем вы узнаете на моих курсах в Школе прикладного бизнес-анализа на базе нашего лицензированного учебного центра обучения и повышения квалификации системных и бизнес-аналитиков в Москве: