REST — это стиль, где система — набор ресурсов с адресами, а действия задаются HTTP-методами; данные обычно в JSON, который читается глазами. gRPC — это RPC (Remote Procedure Call): вы вызываете удалённую функцию так, будто она локальная — «посчитай», «начисли», «проверь». Контракт задаётся строгой схемой в файле .proto, данные едут в бинарном формате Protocol Buffers поверх HTTP/2, по нему же из схемы генерируется код клиента и сервера. gRPC быстрее и компактнее REST и умеет стриминг, но бинарь не прочитать глазами, из браузера напрямую он почти не работает и отлаживать его сложнее. Грубое правило: gRPC — для внутренних сервисов под нагрузкой, REST — для публичного API и браузера.
Однажды я полдня доказывал коллеге, что интеграция «не отвечает». Открываю инструмент, шлю запрос — в ответ нечитаемая каша из байтов, ни одного знакомого поля. Я привык, что API — это JSON, который можно прочитать как письмо. А это был gRPC: бинарный формат, который без схемы .proto вообще не разворачивается в человеческий вид. Сервис отвечал прекрасно — просто я смотрел на него не теми глазами. С этого началось моё понимание, что REST и gRPC — это два разных способа думать о вызове, а не «обычный API» и «какой-то странный».
REST: ресурсы, которые читаются глазами
REST мы подробно разбирали в записи про REST API, поэтому здесь коротко — как точку отсчёта. Главная идея REST: всё, чем оперирует система, — это ресурсы (заказы, пользователи), у каждого есть адрес-URL, а действие задаётся HTTP-методом: GET — прочитать, POST — создать, и так далее. Данные обычно едут в JSON — текстовом формате, который можно открыть и прочитать как обычный текст.
Вы мыслите существительными: «заказ номер 123», «список пользователей». Вы оперируете вещами и применяете к ним стандартные действия. Это и есть «ресурсный» взгляд на мир.
gRPC: вызов удалённой функции как локальной
gRPC мыслит иначе — глаголами. В основе лежит идея RPC (Remote Procedure Call) — «вызов удалённой процедуры». Звучит сложно, а суть простая: вы вызываете функцию, которая живёт на другом сервере, так, будто она лежит у вас под рукой.
Представьте, что у вас в коде есть функция начислитьБонусы(заказ). Обычно она тут же, в вашей программе. RPC говорит: пусть эта функция физически живёт на чужом сервере, но вы вызываете её той же строчкой, как локальную. Всё, что между вашим вызовом и чужим сервером — сеть, упаковка данных, отправка, распаковка ответа — спрятано под капот. Вы не думаете про URL и HTTP-методы, вы думаете «вызвать метод НачислитьБонусы и получить результат».
То есть REST мыслит ресурсами («вот заказ, измени его»), а gRPC — действиями («выполни вот эту операцию»). Это разные способы описать одно и то же взаимодействие, и они тянут за собой разные технические решения.
«g» в gRPC — это не «Google»
Хотя gRPC сделали в Google, официально буква «g» расшифровывается каждый релиз по-разному (шутка авторов) и к названию компании прямого отношения не имеет. Запоминать это не нужно, но если кто-то уверенно скажет «g — это Google» — это распространённое заблуждение. Важна суть: gRPC — это конкретная реализация идеи RPC от Google.
Protocol Buffers и файл .proto: контракт-схема
Чтобы клиент и сервер договорились, какие функции есть и какие у них аргументы, gRPC использует строгую схему — файл с расширением .proto, написанный на языке Protocol Buffers (protobuf). Это и есть контракт: что можно вызвать, что прислать и что вернётся. Аналог OpenAPI в мире REST, только обязательный и строже.
Выглядит .proto-файл так (это обычный текст, не код для исполнения):
syntax = "proto3";
service OrderService {
rpc GetOrder (GetOrderRequest) returns (Order);
rpc CreateOrder (CreateOrderRequest) returns (Order);
}
message GetOrderRequest {
int64 id = 1;
}
message Order {
int64 id = 1;
string status = 2;
int64 amount = 3;
}
Разберём по строкам. service OrderService — это набор удалённых функций, которые можно вызвать. Каждая строка rpc — одна функция: например, GetOrder принимает GetOrderRequest и возвращает Order. Ниже message — это структуры данных: GetOrderRequest содержит одно поле id, а Order — три поля. Цифры после знака равенства (1, 2, 3) — это номера полей: именно по номеру, а не по имени, поле кодируется в бинарь. Поэтому номер нельзя менять у живого контракта — это сразу ломающее изменение, про которое отдельная запись о версионировании API.
Из этого .proto-файла специальный инструмент генерирует код — готовый клиент и заготовку сервера на нужном языке (Java, Go, Python и т.д.). Разработчику не надо вручную писать упаковку и распаковку данных: схема одна, код из неё сгенерирован автоматически на обеих сторонах. Это сильная сторона gRPC: контракт и код всегда совпадают, потому что код сделан из контракта.
Бинарный формат и HTTP/2: почему быстрее
Два технических отличия дают gRPC скорость. Первое — данные едут не текстом, а в бинарном виде. JSON в REST — это человекочитаемый текст: имена полей, кавычки, скобки занимают место и требуют разбора. Protobuf кодирует то же самое набором байтов по номерам полей — компактнее и быстрее упаковывается-распаковывается.
Второе — gRPC работает поверх HTTP/2 (более новой версии протокола), который умеет держать одно соединение для многих параллельных вызовов и поддерживает стриминг — поток сообщений в одну или обе стороны без переустановки связи. Это удобно, например, для подписки на поток событий или передачи данных порциями.
Обратная сторона этих же свойств — бинарь не прочитать глазами в логах и инструментах, а из браузера gRPC напрямую почти не работает: браузеру нужен отдельный прокси-слой (gRPC-Web). Поэтому то, что делает gRPC быстрым внутри, делает его неудобным снаружи.
Как выглядит вызов gRPC
sequenceDiagram
participant C as Клиент (сгенерён из .proto)
participant S as Сервер (gRPC)
C->>C: вызов метода GetOrder(id=123) как локального
C->>S: бинарный запрос по HTTP/2
S->>S: распаковал protobuf, выполнил функцию
S-->>C: бинарный ответ Order{id, status, amount}
C->>C: распаковал в обычный объект
Схема показывает путь одного вызова. Разработчик в своём коде вызывает GetOrder(id=123) — для него это обычная локальная функция, сгенерированная из .proto. Под капотом сгенерированный клиент упаковывает аргументы в бинарный protobuf и отправляет серверу по HTTP/2. Сервер распаковывает запрос, выполняет настоящую функцию, упаковывает результат Order обратно в бинарь и шлёт ответ. Клиент распаковывает его в обычный объект и отдаёт коду как результат вызова. Главное, что нужно вынести: вся упаковка, отправка по сети и распаковка спрятаны — для пишущего код это выглядит как вызов локальной функции, в этом и весь смысл RPC.
REST против gRPC: таблица
| Свойство | REST | gRPC |
|---|---|---|
| Модель | Ресурсы (существительные) | Вызов функций (глаголы) |
| Формат данных | JSON (текст, читается глазами) | Protobuf (бинарь, не читается) |
| Контракт | OpenAPI (желателен) | .proto (обязателен, строгий) |
| Транспорт | HTTP/1.1 или HTTP/2 | HTTP/2 |
| Стриминг | Слабо, обходными путями | Да, из коробки |
| Из браузера | Напрямую | Только через прокси (gRPC-Web) |
| Скорость и размер | Медленнее, объёмнее | Быстрее, компактнее |
| Отладка | Просто (curl, глазами) | Сложнее (нужна схема и инструменты) |
Когда что выбирать
gRPC — для общения сервисов между собой внутри системы, особенно когда их много и нагрузка высокая. Это типичная история микросервисной архитектуры: десятки сервисов гоняют запросы друг к другу, важны скорость, компактность и строгий контракт, который не даст разъехаться. Глазами эти вызовы всё равно никто не смотрит — их видят только машины.
REST — для всего, что выходит наружу: публичный API для партнёров, бэкенд для веб-приложения в браузере, интеграции, где на той стороне неизвестно кто. Здесь важнее простота, читаемость и то, что REST понимают вообще все инструменты и разработчики. Платить скоростью за это почти всегда не жалко. А если у клиента много разных экранов и каждому нужен свой набор полей — есть третий подход, GraphQL, где форму ответа задаёт сам клиент.
Простое правило выбора
Вызов уходит в браузер или наружу к чужим людям → REST. Вызов остаётся между вашими сервисами под нагрузкой и со строгим контрактом → gRPC. Сомневаетесь — начинайте с REST: его проще отладить и понять, а на gRPC переходят осознанно, когда упираются в скорость или в дисциплину контрактов между сервисами.
Где это касается аналитика
Если интеграция на gRPC, контракт — это .proto-файл, и читать его надо уметь: какие методы есть, какие сообщения, какие номера полей. Менять контракт нельзя как попало — переименовать поле или переиспользовать номер ломает обе стороны. Если в требованиях на gRPC-сервис вы не зафиксировали методы и сообщения — спека дырявая ровно так же, как REST-спека без описания эндпоинтов.
Откуда это взялось
Сама идея RPC старая — «вызывать удалённую функцию как локальную» обсуждали ещё в начале 1980-х, а в 1990-х были CORBA и SOAP, тяжёлые и церемонные. Protocol Buffers Google сделал для себя около 2008 года, чтобы компактно и быстро гонять данные между своими сервисами. Сам gRPC Google выложил в открытый код в 2015 году — это была публичная версия внутреннего инструмента под названием Stubby, которым в компании уже годами связывали сервисы. gRPC взлетел вместе с микросервисами и Kubernetes: когда сервисов становятся сотни, цена каждого лишнего байта и каждой миллисекунды складывается в реальные деньги.
Частые вопросы
Что такое gRPC простыми словами?
gRPC — это способ одной программе вызвать функцию, которая физически живёт на другом сервере, так, будто она локальная: «выполни вот эту операцию и верни результат». В основе лежит идея RPC (вызов удалённой процедуры). Контракт описывается строгой схемой в файле .proto, данные едут в компактном бинарном формате Protocol Buffers поверх HTTP/2, а код клиента и сервера генерируется из схемы автоматически. В отличие от REST, который мыслит ресурсами, gRPC мыслит вызовами функций.
gRPC или REST — что выбрать?
Берите gRPC для общения сервисов внутри системы под нагрузкой, где важны скорость, компактность и строгий контракт, — это типичный микросервисный сценарий. Берите REST для публичного API, для браузера и для интеграций с внешними людьми: он проще, читается глазами, понятен всем инструментам и работает напрямую из браузера, чего gRPC без прокси не умеет. Если сомневаетесь — начинайте с REST, он проще в отладке.
Что такое protobuf?
Protocol Buffers (protobuf) — это язык описания строгой схемы данных и компактный бинарный формат для их передачи, на котором работает gRPC. В файле .proto описывают, какие есть удалённые функции (service и rpc) и какие структуры данных они принимают и возвращают (message с пронумерованными полями). Из этого файла автоматически генерируется код клиента и сервера, поэтому контракт и реализация всегда совпадают. Бинарный формат компактнее и быстрее JSON, но не читается глазами без схемы.