Прямой ответ
gRPC — RPC-фреймворк поверх HTTP/2 с контрактом в protobuf: .proto-файл описывает сервисы и сообщения, кодогенератор выдаёт типизированные заглушки для клиента и сервера (Python, Go, Java — из одного контракта), а передача — бинарная и компактная (заметно легче JSON). Оправдан для внутренних сервис-сервисных вызовов: строгий контракт с версионированием полей, потоковая передача (streaming), высокая частота вызовов. Для публичных API с браузерными клиентами остаётся REST/JSON (браузер не умеет gRPC без прослойки grpc-web; отладка curl'ом — тоже аргумент). Ключевая дисциплина: контракт первичен — изменения начинаются с .proto и совместимости полей, а не с кода.
Контракт: .proto как источник истины
service Orders { rpc GetOrder(GetOrderRequest) returns (Order); } + сообщения с типизированными полями и номерами — и кодогенерация (grpcio-tools: python -m grpc_tools.protoc ...) создаёт *_pb2.py (сообщения) и *_pb2_grpc.py (заглушки). Правила эволюции контракта — главное, что нужно знать: номера полей неизменны (по ним идёт wire-совместимость: добавлять поля можно, переиспользовать/менять номера удалённых — ломает старые клиенты); optional-семантика по умолчанию (отсутствие поля ≠ нулевое значение — для явного различения есть presence); enum с нулевым значением-дефолтом; новые поля — additive, удаление — deprecated-пометка, физическое удаление позже. Контракт живёт в репозитории (или отдельном contract-репо) и ревьюится как код — это прямой аналог контрактной разработки OpenAPI (сравнение подходов).
Четыре типа вызовов и когда какой
Unary (запрос-ответ) — обычный RPC, замена GET/POST. Server streaming — один запрос, поток ответов (выгрузка, подписка на события, прогресс длинной операции). Client streaming — поток запросов, один ответ (загрузка батча). Bidirectional — два независимых потока (чат, игровые сессии, интерактивные протоколы). Стриминг — то, ради чего часто берут gRPC: на HTTP/2 потоки мультиплексированы, накладные расходы на сообщение минимальны; но клиентская логика усложняется (обработка прерванного потока, backpressure), и «стриминг везде» — антипаттерн: unary проще отлаживать и понимать. Выбор — по природе взаимодействия, не по моде.
Сервер и клиент на Python
Сервер: класс-наследник сгенерированного Servicer, реализация методов (context.abort(grpc.StatusCode.NOT_FOUND, "...") — ошибки; статусы gRPC — собственный словарь, маппинг с HTTP-семантикой один-в-один не переносится), grpc.server(ThreadPoolExecutor) (sync) или grpc.aio (async-сервер — для I/O-нагруженных сервисов; механика async — в статье про asyncio). Клиент: канал (grpc.insecure_channel в dev, TLS в проде) + заглушка; deadline/timeout на каждый вызов (timeout=5 — обязательная дисциплина: RPC без дедлайна — висящий поток у обеих сторон; дедлайн распространяется по цепочке сервисов). Повторы — только идемпотентным методам, с backoff; health checking и load balancing — через стандартные механизмы gRPC (health proto, round_robin) или сервис-меш.
Интерцепторы, ошибки, наблюдаемость
Интерцепторы — middleware gRPC: логирование вызовов, метрики (latency/count по методам), аутентификация (метаданные-токены — проверка в интерцепторе; семантика токенов — в статье про JWT/OAuth), трейсинг (OpenTelemetry-интеграции для распространения контекста между сервисами — основа распределённой трассировки в микросервисах). Ошибки — структурированно: код статуса + message + details (google.rpc.Status с произвольными payload) — «просто строка» в message неудобна машинам. Отладка: grpcurl (аналог curl для gRPC — по reflection), логи интерцептора, health-эндпоинт для оркестратора.
Когда gRPC, а когда REST
gRPC выигрывает: внутренние сервисы с частыми вызовами (бинарность + HTTP/2 — меньше CPU и latency), строгие контракты между командами (protobuf дисциплинирует), стриминг, полиглотные системы (один .proto — клиенты на всех языках). REST выигрывает: публичные API (браузеры, curl, кеширование HTTP, прокси), редкие вызовы, человеческая отладка, интеграции с внешним миром. Частая гибридная архитектура: внешний край — REST/JSON (шлюз), внутри — gRPC между сервисами; REST/gRPC-транскодинг (grpc-gateway) даёт оба входа из одного контракта. Решение принимается по нагрузке и аудитории API, а не по «современности» протокола — дизайн ресурсов и статусы из REST-практики применимы к gRPC-методам на уровне семантики.
Практический сценарий: контракт и сервис уведомлений
Минимальный, но полный цикл gRPC-разработки. В contract-репозитории живёт notify/v1/notify.proto: сервис Notifier с unary-методом Send(SendRequest) returns (SendResponse) и server-streaming Subscribe(SubscribeRequest) returns (stream Event); у сообщений — поля с номерами и optional там, где отсутствие осмысленно. Из контракта кодогенерация (команда в Makefile) выдаёт заглушки для Python-сервиса и Go-клиента — один контракт, два языка, ноль расхождений. Python-сервер: NotifyServicer валидирует вход, при неизвестном канале — context.abort(INVALID_ARGUMENT, "unknown channel") с машиночитаемым кодом в details; дедлайны проверяются (context.time_remaining()) — долгая отправка прерывается честно, а не висит. Клиент вызывает с timeout=3 и ретраем только на UNAVAILABLE (идемпотентно — сервер дедуплицирует по idempotency_key из запроса). Эволюция: добавили поле priority = 4 — старый клиент его игнорирует, новый сервер понимает отсутствие как «обычный»; удаление поля — только через reserved 4 в proto, чтобы номер не переиспользовался. Наблюдаемость — интерцептор с метриками по методам и трейс-контекстом. Весь сервис — контракт на пятьдесят строк плюс двести строк реализации, и ни одна деталь поведения не «договорена на словах».
FAQ
Насколько protobuf компактнее JSON? Типично в 3–10 раз на структурированных данных (бинарные теги вместо имен полей, varint для чисел) плюс скорость парсинга. Для редких вызовов это незаметно; для тысяч сообщений в секунду — существенная экономия CPU и сети.
Можно ли без кодогенерации? Практически нет: сообщения и заглушки генерируются из .proto, и это фича (контракт = код). Ручное написание pb-классов — путь к рассинхрону; генерация встраивается в сборку (make/CI).
Как версионировать gRPC-сервис?
Пакет в .proto (orders.v1) + additive-изменения внутри версии; ломающие — новый пакет v2 с сосуществованием (сервер поднимает оба сервиса). Номера полей — не версия, а wire-идентичность.
gRPC сложен для команды — есть альтернатива со строгим контрактом? REST + OpenAPI с генерацией клиентов даёт 80% дисциплины при знакомом стеке (контрактная разработка). gRPC берите, когда нужны стриминг/производительность/полиглотность — иначе JSON-over-HTTP проще в эксплуатации.
Источники: документация gRPC (Python, deadlines), protobuf.dev — ссылки в шапке.