Начало работы с gRPC-Python

1. Введение

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

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

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

Сгенерированный код учитывает не только сложности взаимодействия между сервером и клиентом, но и сериализацию и десериализацию данных.

Что вы узнаете

  • Как использовать Protocol Buffers для определения API сервиса.
  • Как создать клиент и сервер на основе gRPC из определения Protocol Buffers с помощью автоматической генерации кода.
  • Понимание взаимодействия клиент-сервер с использованием gRPC.

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

2. Прежде чем начать

Что вам понадобится

  • Python 3.9 или выше. Мы рекомендуем Python 3.13. Инструкции по установке для конкретной платформы см. в разделе «Настройка и использование Python» . В качестве альтернативы можно установить Python, не являющийся системным, используя такие инструменты, как uv или pyenv .
  • Используйте pip для установки пакетов Python.
  • venv для создания виртуальных сред Python.

Пакеты ensurepip и venv являются частью стандартной библиотеки Python и обычно доступны по умолчанию.

Однако некоторые дистрибутивы на основе Debian (включая Ubuntu) исключают их при распространении Python. Для установки пакетов выполните следующую команду:

sudo apt install python3-pip python3-venv

Получите код

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

grpc-codelabs

Исходный код для этого практического занятия доступен в каталоге codelabs/grpc-python-getting-started/start_here . Если вы предпочитаете не реализовывать код самостоятельно, готовый исходный код доступен в каталоге completed .

Сначала создайте рабочую директорию codelab и перейдите в неё с помощью команды cd:

mkdir grpc-python-getting-started && cd grpc-python-getting-started

Скачайте и распакуйте CodeLab:

curl -sL https://github.com/grpc-ecosystem/grpc-codelabs/archive/refs/heads/v1.tar.gz \
  | tar xvz --strip-components=4 \
  grpc-codelabs-1/codelabs/grpc-python-getting-started/start_here

В качестве альтернативы вы можете скачать ZIP-архив, содержащий только папку codelab, и распаковать его вручную.

3. Определите услугу.

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

  • Метод RPC под названием GetFeature , который реализует сервер, а клиент вызывает.
  • Типы сообщений Point и Feature представляют собой структуры данных, которыми обмениваются клиент и сервер при использовании метода GetFeature . Клиент предоставляет координаты карты в виде Point в своем запросе GetFeature к серверу, а сервер отвечает соответствующим Feature , описывающим то, что находится по этим координатам.

Данный RPC-метод и типы сообщений для него будут определены в файле protos/route_guide.proto предоставленного исходного кода.

Протоколы Protocol Buffers обычно называются protobuf. Для получения дополнительной информации о терминологии gRPC см. раздел «Основные концепции, архитектура и жизненный цикл gRPC».

Типы сообщений

В файле protos/route_guide.proto исходного кода сначала определите тип сообщения Point . Point представляет собой пару координат широты и долготы на карте. Для этого практического задания используйте целые числа для координат:

message Point {
  int32 latitude = 1;
  int32 longitude = 2;
}

Цифры 1 и 2 — это уникальные идентификационные номера для каждого поля в структуре message .

Далее определите тип сообщения Feature . В Feature используется string поле для имени или почтового адреса объекта, расположенного в точке Point :

message Feature {
  // The name or address of the feature.
  string name = 1;

  // The point where the feature is located.
  Point location = 2;
}

Метод обслуживания

В файле route_guide.proto содержится структура service с именем RouteGuide , которая определяет один или несколько методов, предоставляемых сервисом приложения.

Добавьте rpc метод GetFeature в определение RouteGuide . Как объяснялось ранее, этот метод будет искать название или адрес местоположения по заданному набору координат, поэтому пусть GetFeature возвращает Feature для заданной Point :

service RouteGuide {
  // Definition of the service goes here

  // Obtains the feature at a given position.
  rpc GetFeature(Point) returns (Feature) {}
}

Это унарный RPC-метод: простой RPC-вызов , при котором клиент отправляет запрос на сервер и ожидает ответа, подобно вызову локальной функции.

4. Сгенерируйте код клиента и сервера.

Далее, используя компилятор Protocol Buffer, сгенерируйте шаблонный gRPC-код для клиента и сервера из файла .proto .

Для генерации кода gRPC на Python мы создали grpcio-tools . Он включает в себя:

  1. Стандартный компилятор protoc , генерирующий код Python на основе определений message .
  2. Плагин gRPC protobuf, генерирующий код Python (заглушки для клиента и сервера) на основе определений service .

Мы установим пакет grpcio-tools для Python с помощью pip. Давайте создадим новое виртуальное окружение Python (venv), чтобы изолировать зависимости вашего проекта от системных пакетов:

python3 -m venv --upgrade-deps .venv

Для активации виртуального окружения в оболочке bash/zsh:

source .venv/bin/activate

Для Windows и нестандартных оболочек см. таблицу по адресу https://docs.python.org/3/library/venv.html#how-venvs-work .

Далее установите grpcio-tools (это также установит пакет grpcio ):

pip install grpcio-tools

Для генерации шаблонного кода на Python используйте следующую команду:

python -m grpc_tools.protoc --proto_path=./protos  \
 --python_out=. --pyi_out=. --grpc_python_out=. \
 ./protos/route_guide.proto

В результате будут сгенерированы следующие файлы для интерфейсов, определенных в route_guide.proto :

  1. route_guide_pb2.py содержит код , который динамически создает классы, генерируемые на основе определений message .
  2. route_guide_pb2.pyi — это «файл-заглушка» или «файл подсказок типов», созданный на основе определений message . Он содержит только сигнатуры без какой-либо реализации. Файлы-заглушки могут использоваться IDE для улучшения автозавершения кода и обнаружения ошибок.
  3. route_guide_pb2_grpc.py генерируется на основе определений service и содержит классы и функции, специфичные для gRPC.

Специализированный код gRPC содержит:

  1. RouteGuideStub , который может использоваться gRPC-клиентом для вызова RPC-вызовов RouteGuide.
  2. RouteGuideServicer , который определяет интерфейс для реализаций сервиса RouteGuide .
  3. Функция add_RouteGuideServicer_to_server используется для регистрации RouteGuideServicer на gRPC-сервере .

5. Создайте сервис.

Для начала давайте рассмотрим, как создать сервер RouteGuide . Создание и запуск сервера RouteGuide состоит из двух этапов:

  • Реализация интерфейса сервиса, сгенерированного на основе определения нашего сервиса, с функциями, которые выполняют фактическую «работу» сервиса.
  • Запуск gRPC-сервера на определенном порту для приема запросов от клиентов и отправки ответов.

Исходный сервер RouteGuide можно найти в start_here/route_guide_server.py .

Внедрить RouteGuide

В route_guide_server.py есть класс RouteGuideServicer , который наследует сгенерированный класс route_guide_pb2_grpc.RouteGuideServicer :

# RouteGuideServicer provides an implementation
# of the methods of the RouteGuide service.
class RouteGuideServicer(route_guide_pb2_grpc.RouteGuideServicer):

RouteGuideServicer реализует все методы сервиса RouteGuide .

Давайте подробно рассмотрим простую реализацию RPC. Метод GetFeature получает Point от клиента и возвращает соответствующую информацию о объекте из его базы данных в Feature .

def GetFeature(self, request, context):
    feature = get_feature(self.db, request)
    if feature is None:
        return route_guide_pb2.Feature(name="", location=request)
    else:
        return feature

Методу передается запрос route_guide_pb2.Point для RPC-вызова и объект grpc.ServicerContext , предоставляющий информацию, специфичную для RPC, например, ограничения по времени ожидания. Он возвращает ответ route_guide_pb2.Feature .

Запуск сервера

После реализации всех методов RouteGuide следующим шагом будет запуск gRPC-сервера, чтобы клиенты могли фактически использовать ваш сервис:

def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    route_guide_pb2_grpc.add_RouteGuideServicer_to_server(
        RouteGuideServicer(),
        server,
    )
    listen_addr = "localhost:50051"
    server.add_insecure_port(listen_addr)
    print(f"Starting server on {listen_addr}")
    server.start()
    server.wait_for_termination()

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

6. Создайте клиента.

В этом разделе мы рассмотрим создание клиента для нашего сервиса RouteGuide . Начальный код клиента можно посмотреть в start_here/route_guide_client.py .

Создать заглушку

Для вызова методов сервиса нам сначала нужно создать заглушку .

Мы создаём экземпляр класса RouteGuideStub модуля route_guide_pb2_grpc , сгенерированного из нашего .proto файла внутри файла route_guide_client.py .

channel = grpc.insecure_channel("localhost:50051")
stub = route_guide_pb2_grpc.RouteGuideStub(channel)

Способы вызова сервиса

Для методов RPC, возвращающих один ответ (известных как методы с унарным ответом), gRPC Python поддерживает как синхронную (блокирующую), так и асинхронную (неблокирующую) семантику управления потоком.

Простой RPC

Для начала определим Point с помощью которой будет вызываться сервис. Это должно быть так же просто, как создание объекта из пакета route_guide_pb2 с некоторыми свойствами:

point = route_guide_pb2.Point(latitude=412346009, longitude=-744026814)

Синхронный вызов простого RPC- GetFeature почти так же прост, как вызов локального метода. RPC-вызов ожидает ответа от сервера и либо возвращает ответ, либо генерирует исключение. Мы можем вызвать метод и увидеть ответ примерно так:

feature = stub.GetFeature(point)
print(feature)

Вы можете просмотреть поля объекта Feature и вывести результат запроса:

if feature.name:
    print(f"Feature called '{feature.name}' at {format_point(feature.location)}")
else:
    print(f"Found no feature at at {format_point(feature.location)}")

7. Попробуйте.

Запустите сервер:

python route_guide_server.py

С другого терминала снова активируйте виртуальную среду, затем запустите клиент:

python route_guide_client.py

В результате вы увидите примерно такой вывод, при этом временные метки для наглядности опущены:

name: "16 Old Brook Lane, Warwick, NY 10990, USA"
location {
  latitude: 412346009
  longitude: -744026814
}

Feature called '16 Old Brook Lane, Warwick, NY 10990, USA' at latitude: 412346009, longitude: -744026814

8. Что дальше?