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

1. Введение

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

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

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

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

Чему вы научитесь

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

Эта практическая работа предназначена для разработчиков Python, впервые использующих 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 с помощью плагина компилятора буфера протокола grpc_tools.protoc .

grpc-codelabs

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

Сначала создайте рабочий каталог codelab и перейдите в него:

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

Загрузите и распакуйте кодовую лабораторию:

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 предоставленного исходного кода.

Буферы протоколов обычно называются 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. Сгенерируйте клиентский и серверный код.

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

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

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

Мы установим пакет Python grpcio-tools с помощью 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()

Метод server.start 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. Что дальше?