QA Interview Hub · База знань · Тести
API
gRPC
gRPC: що це і чим відрізняється від REST
gRPC — фреймворк віддалених викликів (RPC) від Google: клієнт викликає метод сервісу так, ніби це локальна функція. Контракт описується у .proto-файлі, з нього генеруються клієнт і серверні заглушки для багатьох мов. Транспорт — HTTP/2, формат повідомлень за замовчуванням — Protocol Buffers (бінарний, компактний, зі строгою схемою).
syntax = "proto3";
package shop.v1;
service OrderService {
rpc GetOrder (GetOrderRequest) returns (Order);
rpc WatchOrders (WatchRequest) returns (stream Order);
}
message GetOrderRequest { string id = 1; }
message Order {
string id = 1;
int64 total_cents = 2;
Status status = 3;
}
enum Status { STATUS_UNSPECIFIED = 0; PAID = 1; SHIPPED = 2; }
Порівняння з REST і GraphQL:
- Контракт. gRPC — contract-first, схема обов'язкова й типізована; у REST схема (OpenAPI) необов'язкова й часто розходиться з реалізацією;
- Модель. gRPC — виклики методів (
GetOrder), REST — ресурси й HTTP-дієслова, GraphQL — запит довільної форми до одного ендпоінта;
- Продуктивність. Бінарний формат і мультиплексування HTTP/2 дають менший розмір і затримку, ніж JSON поверх HTTP/1.1;
- Стрімінг. gRPC підтримує його «з коробки», у REST це окремі рішення (SSE, WebSocket);
- Браузер. gRPC не працює в браузері напряму (потрібні grpc-web або Connect і проксі), REST працює скрізь;
- Налагодження. JSON можна глянути в браузері чи
curl, бінарний gRPC потребує схеми та інструментів (grpcurl, Postman).
Де застосовують: внутрішня взаємодія мікросервісів, низька затримка, стрімінг, мобільні клієнти, поліглотні команди. Публічні API для сторонніх розробників частіше лишають REST.
Protobuf: сумісність схеми, яку ламати не можна
У бінарному повідомленні поле впізнається за номером, а не за іменем. Тому номер — частина контракту: клієнти й сервери різних версій працюють одночасно, і схема має лишатися сумісною в обидва боки (backward: нові сервери читають старі повідомлення; forward: старі читають нові).
Безпечні зміни:
- додати нове поле з новим номером (старі клієнти його ігнорують, нові отримують значення за замовчуванням від старих серверів);
- додати нове значення enum, новий метод чи новий сервіс;
- перейменувати поле: у бінарному форматі нічого не змінюється, але ламаються JSON-представлення та вихідний код клієнтів, тому перевіряйте це окремо.
Небезпечні зміни:
- змінити номер поля або повторно використати номер видаленого поля: старі дані прочитаються як зовсім інше поле;
- змінити тип на несумісний на рівні wire-формату (наприклад,
string на int32); деякі цілі типи (int32, uint32, int64, bool) взаємно сумісні, але зі зрізанням значень;
- перейти від
repeated до скалярного поля чи навпаки, змінити oneof так, що поле опиняється в групі або виходить з неї;
- видалити поле без
reserved.
message User {
reserved 4, 7;
reserved "legacy_phone";
string id = 1;
string email = 2;
optional string nickname = 3;
}
Значення за замовчуванням. У proto3 скалярне поле з нульовим значенням (0, пустий рядок, false) не передається, і отримувач не відрізняє «не задано» від «0». Якщо ця різниця важлива (наприклад, «знижка 0%» проти «знижка не вказана»), використовують optional (є ознака присутності, hasX) або обгортки типу google.protobuf.Int32Value. Перше значення enum має бути нульовим і зазвичай називається *_UNSPECIFIED.
Невідомі поля (які новіша схема додала, а старий код не знає) сучасні реалізації зберігають і передають далі, а не гублять. Номери 1–15 кодуються одним байтом тегу, тож їх лишають для найчастіших полів.
Типи викликів, статуси, дедлайни й метадані
Чотири типи RPC (HTTP/2 дозволяє багато потоків в одному з'єднанні):
- Unary: один запит, одна відповідь (найпоширеніший, найближчий до REST);
- Server streaming: один запит, потік відповідей (стрічка подій, великі вибірки);
- Client streaming: потік запитів, одна відповідь наприкінці (завантаження файлу, пачка метрик);
- Bidirectional streaming: обидва боки шлють потоки незалежно (чат, інтерактивні сесії).
Статуси. Незалежно від результату HTTP-статус відповіді — 200, а справжній результат приходить у trailers як grpc-status (число) і grpc-message. Тому тестувати треба код gRPC, а не HTTP. Основні:
OK (0); CANCELLED (1); UNKNOWN (2);
INVALID_ARGUMENT (3): запит некоректний за змістом (≈ HTTP 400);
DEADLINE_EXCEEDED (4): вичерпано дедлайн (≈ 504);
NOT_FOUND (5), ALREADY_EXISTS (6);
PERMISSION_DENIED (7): автентифікований, але прав немає (≈ 403);
RESOURCE_EXHAUSTED (8): ліміти, квоти, завеликий меседж (≈ 429);
FAILED_PRECONDITION (9): стан системи не дозволяє операцію;
UNIMPLEMENTED (12): метод не реалізовано; INTERNAL (13): помилка сервера;
UNAVAILABLE (14): сервіс тимчасово недоступний, можна повторити (≈ 503);
UNAUTHENTICATED (16): немає чинних облікових даних (≈ 401).
Деталі помилки (наприклад, які поля невалідні) передаються структурою google.rpc.Status з типізованими details (BadRequest, ErrorInfo) у заголовку grpc-status-details-bin.
Метадані — аналог HTTP-заголовків: пари ключ-значення на початку (headers) і наприкінці виклику (trailers). Ключі в нижньому регістрі, бінарні мають суфікс -bin, префікс grpc- зарезервований. Через метадані передають токен (authorization: Bearer …), ідентифікатор трасування, мову.
Дедлайн — абсолютний час, до якого клієнт готовий чекати; на мережі він передається як grpc-timeout. За замовчуванням дедлайну немає, тож виклик може висіти нескінченно: у продакшн-клієнтів його задають завжди. Дедлайн має поширюватися далі по ланцюжку сервісів, а після його закінчення сервер повинен припинити роботу. Ретраї налаштовують у service config (retryPolicy: кількість спроб, backoff, перелік повторюваних статусів, зазвичай UNAVAILABLE); повторювати варто лише ідемпотентні виклики. Інтерсептори (middleware) обгортають виклики на клієнті й сервері: логування, авторизація, метрики, трасування.
Як QA тестує gRPC-сервіс
Інструменти.
- grpcurl: «curl для gRPC». Якщо на сервері ввімкнено server reflection, схема береться з нього; інакше передають
-proto та -import-path. Метадані: -H;
- Postman / Insomnia: gRPC-запити з імпортом
.proto або через reflection, зручно для ручного дослідження й демонстрацій;
- Згенерований клієнт у тест-фреймворку. Для TypeScript:
@grpc/grpc-js з @grpc/proto-loader або типізований код із ts-proto, Buf/Connect. Це основний варіант для автотестів: типи ловлять розбіжності зі схемою ще під час компіляції;
- buf:
buf lint і buf breaking --against '.git#branch=main' у CI показують, чи не зламав PR зворотну сумісність схеми;
- Навантаження:
ghz або k6 (модуль k6/net/grpc).
# список сервісів і опис методу (потрібна reflection)
grpcurl -plaintext localhost:50051 list
grpcurl -plaintext localhost:50051 describe shop.v1.OrderService
# виклик з метаданими
grpcurl -plaintext -H 'authorization: Bearer TOKEN' \
-d '{"id": "42"}' localhost:50051 shop.v1.OrderService/GetOrder
const deadline = new Date(Date.now() + 2000);
client.getOrder({ id: '42' }, meta, { deadline }, (err, order) => {
expect(err?.code).toBe(grpc.status.NOT_FOUND);
});
Що перевіряти:
- Позитивні сценарії і значення всіх полів відповіді, включно з дефолтами та
optional;
- Валідацію: порожні/завеликі/невалідні поля дають
INVALID_ARGUMENT з коректними деталями, а не INTERNAL чи UNKNOWN;
- Коди помилок:
NOT_FOUND, ALREADY_EXISTS, PERMISSION_DENIED проти UNAUTHENTICATED (відсутній, прострочений, чужий токен);
- Дедлайни: повільна залежність дає
DEADLINE_EXCEEDED, сервер не продовжує роботу після скасування;
- Стрімінг: порядок повідомлень, завершення потоку, скасування клієнтом, обрив посередині, backpressure, порожній потік;
- Великі повідомлення: ліміт розміру (за замовчуванням приймання зазвичай 4 МБ) дає
RESOURCE_EXHAUSTED, а не падіння сервера;
- Сумісність: старий клієнт зі свіжим сервером і навпаки, невідомі поля й значення enum;
- Ідемпотентність і ретраї: повторний
UNAVAILABLE-виклик не створює дублів;
- Безпека: TLS/mTLS, авторизація на рівні методів, reflection вимкнено у продакшні (або закрито автентифікацією).
Браузер і шлюзи. Браузерний JS не керує кадрами та trailers HTTP/2, тому прямий gRPC недоступний. Рішення: grpc-web через проксі (наприклад, Envoy), протокол Connect, або gRPC-Gateway, який надає REST/JSON поверх gRPC-сервісу. Тести слід проганяти й на шлюзі, бо там своє відображення статусів у HTTP-коди та своя серіалізація JSON.