Практикум: клиентские интерцепторы gRPC и потоковые RPC¶
Воркер выгрузок вызывает настоящий локальный сервис отчётов. Проверяем регистрацию интерцепторов, результат потокового вызова, контекст и завершение работы. Пример к статье «Почему интерцепторы gRPC ломаются на потоковых RPC».
Запуск¶
Установите uv и запустите команды из корня репозитория сайта. При необходимости uv загрузит Python и зависимости. RPC используют только локальный адрес и автоматически выбранный порт.
cd docs/blog/lab/2026-09-07-grpc-interceptors-and-streams
uv run --no-project --python 3.13 --with-requirements requirements.txt python interceptors_lab.py
uv run --no-project --python 3.13 --with-requirements requirements.txt python probe.py
Зафиксированы grpc-client-kit==0.4.0, grpcio==1.84.0; используется Python 3.13. Байтовые сообщения и обычные обработчики позволяют обойтись без генерации protobuf-кода. Здесь проверяется поведение клиентских интерцепторов; это не замер производительности и не конфигурация развёртывания.
Проверки¶
| Сценарий | Что должно выполняться |
|---|---|
| Один объект с четырьмя базовыми классами | В этой версии grpcio запускается только unary-unary |
| Один логический интерцептор, четыре адаптера | Каждый вид RPC создаёт одну запись об успешном результате |
| Таймер вокруг continuation | Действительно запускается, заканчивает до первой строки и пропускает ошибку сервера, которая приходит позже |
| Таймер вокруг итерации | Учитывает ошибку посреди потока; сохраняет статус, детали и завершающие метаданные |
| Библиотечный наблюдатель | Пять строк при успехе; две строки и UNAVAILABLE при сбое |
| Досрочный выход | Явная отмена останавливает серверный обработчик и даёт одну запись CANCELLED |
break без отмены |
Удерживаемый вызов остаётся активным до явной отмены |
| Быстрый поток | Наблюдатель может завершиться до продолжения итерации после последней строки |
Потоковый запрос через write() |
После done_writing() приходит ответ; интерцептор не блокирует отправку |
| Область действия контекста | Токен сбрасывается для всех четырёх видов RPC; контекст вызывающего кода не меняется |
RuntimeError при завершении интерцептора |
Четыре успешных вызова и один UNAVAILABLE сохраняют результат; пять сбоев попадают в лог |
Оба скрипта завершаются с ошибкой при нарушении утверждения или необработанной ошибке asyncio. Завершение ожидается через очереди и события с таймаутами, без подбора пауз. Задержки на сервере имитируют подготовку строк; длительности печатаются для сравнения и могут меняться между запусками.
Файлы¶
report_service.py: четыре обработчика RPC, сценарии сбоя и отмены, запуск и остановка сервера.observers.py: обычные и логические интерцепторы из статьи.consumer.py: полная загрузка и контекст потребителя с отменой при досрочном выходе.interceptors_lab.py: потоковые сценарии и проверка интерфейса вызова.probe.py: изоляция контекста и логирование ошибок завершающего кода.requirements.txt: точные версии библиотек.
Небольшие очереди наблюдений служат для проверок. Обработка данных приложением может продолжаться после завершения RPC, поэтому всю выгрузку измеряйте отдельно. Значение контекста внутри интерцептора не становится автоматически значением у потребителя или полем метаданных gRPC.