Перейти к содержимому

gRPC vs REST

gRPC vs REST: gRPC быстрее (бинарный protobuf, HTTP/2, стриминг, типизация), но сложнее. REST проще (JSON, HTTP/1.1, кеширование, браузеры). Выбор: gRPC для внутренних микросервисов, REST для публичных API.

Введение: Два подхода к общению

В мире API есть два популярных способа общения между сервисами. REST долгое время был стандартом де-факто. Он прост, понятен, использует HTTP/1.1 и JSON. Но у него есть ограничения: он медленный, не поддерживает потоковую передачу, каждое соединение обрабатывает один запрос.

gRPC — более молодой протокол от Google. Он использует HTTP/2, бинарную сериализацию (protobuf) и поддерживает стриминг. Он быстрее, но сложнее.

REST — это как “почта”: вы отправляете письмо (запрос), вам приходит ответ (письмо). Всё просто, но каждое письмо — отдельное.

gRPC — это как “телефонный разговор”: вы устанавливаете соединение и можете обмениваться сообщениями в реальном времени, параллельно, без задержек.

REST (Representational State Transfer) — архитектурный стиль для построения API поверх HTTP. Использует HTTP методы (GET, POST, PUT, DELETE), статус-коды, и обычно JSON.

gRPC (gRPC Remote Procedure Call) — высокопроизводительный фреймворк для удалённого вызова процедур. Использует Protocol Buffers, HTTP/2, и поддерживает стриминг.

Выбор между gRPC и REST — это не вопрос “что лучше”, а вопрос “что подходит для вашей задачи”. Они решают разные проблемы.

Сравнение по ключевым параметрам

ПараметрRESTgRPC
ТранспортHTTP/1.1 (обычно)HTTP/2
Формат данныхJSON, XML, текст, HTMLProtocol Buffers (protobuf)
СериализацияТекстоваяБинарная
Человеко-читаемыйДа (curl, браузер)Нет (бинарный)
ТипизацияСлабая (динамическая)Строгая (статическая, .proto)
КонтрактOpenAPI (опционально).proto (обязателен)
Генерация кодаОпционально (OpenAPI генераторы)Да (из .proto)
СтримингНет (только WebSockets/SSE)Да (server, client, bidirectional)
МультиплексированиеНет (одно соединение → один запрос)Да (одно соединение → много потоков)
КешированиеОтличное (HTTP кеш, CDN)Нет (POST всегда)
БраузерыОтличная поддержкаПлохая (требуется gRPC-Web)
ПроизводительностьСредняяВысокая
Размер сообщенияБольшой (JSON)Маленький (protobuf)
СложностьНизкаяВысокая

Формат данных: JSON vs Protobuf

REST: JSON (текстовый)

{
    "id": 123,
    "name": "Иван Петров",
    "email": "ivan@example.com",
    "age": 30,
    "address": {
        "city": "Москва",
        "street": "Тверская",
        "zip": 101000
    }
}

Размер: ~150 байт

gRPC: Protobuf (бинарный)

message User {
    int32 id = 1;
    string name = 2;
    string email = 3;
    int32 age = 4;
    Address address = 5;
}

message Address {
    string city = 1;
    string street = 2;
    int32 zip = 3;
}

Размер (бинарный): ~60 байт

Сравнение

АспектJSONProtobuf
ЧитаемостьВысокая (отладка curl)Низкая (бинарный)
РазмерБольшойМаленький (в 2-3 раза)
Скорость парсингаМедленная (текст)Быстрая (бинарный)
СхемаОпциональна (JSON Schema)Обязательна (.proto)

Протокол: HTTP/1.1 vs HTTP/2

REST: HTTP/1.1

    sequenceDiagram
    participant Client
    participant Server
    
    Client->>Server: Соединение 1: запрос A
    Server-->>Client: Ответ A
    
    Client->>Server: Соединение 1: запрос B
    Server-->>Client: Ответ B
  

Проблемы:

  • Head-of-line blocking (запрос B ждёт A)
  • Много соединений (каждое со своим handshake)
  • Заголовки не сжимаются
  • Текстовый формат

gRPC: HTTP/2

    sequenceDiagram
    participant Client
    participant Server
    
    Client->>Server: Соединение 1: запрос A (поток 1)
    Client->>Server: Соединение 1: запрос B (поток 2)
    
    Server-->>Client: Ответ B (поток 2)
    Server-->>Client: Ответ A (поток 1)
  

Преимущества:

  • Мультиплексирование (один запрос не блокирует другие)
  • Одно соединение (меньше накладных расходов)
  • Сжатие заголовков (HPACK)
  • Бинарный протокол

Типизация: Слабая vs Строгая

REST: Слабая типизация

// Что такое id? Строка? Число?
// Что будет, если поле отсутствует?
// Нет контракта.
{
    "id": 123,
    "name": "Иван"
}

Проблемы:

  • Ошибки типов в рантайме
  • Клиент должен угадывать
  • Сложная автогенерация клиентов

gRPC: Строгая типизация

message User {
    int32 id = 1;     // id — целое число, обязательно
    string name = 2;  // name — строка
}

Преимущества:

  • Ошибки на этапе компиляции
  • Автогенерация типизированных клиентов
  • Контракт обязателен

Контракт: OpenAPI vs .proto

REST: OpenAPI (опционально)

openapi: 3.0.0
paths:
  /users/{id}:
    get:
      parameters:
        - name: id
          in: path
          schema:
            type: integer
      responses:
        '200':
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: integer
                  name:
                    type: string

Проблемы:

  • Опционально (может не быть)
  • Не синхронизируется с кодом (часто расходится)
  • Генерация клиентов — внешний шаг

gRPC: .proto (обязателен)

service UserService {
    rpc GetUser (GetUserRequest) returns (User);
}

message GetUserRequest {
    int32 user_id = 1;
}

message User {
    int32 id = 1;
    string name = 2;
}

Преимущества:

  • Обязателен (без него ничего не работает)
  • Единый источник истины
  • Генерация клиентов и сервера из одного файла

Стриминг: REST vs gRPC

REST: Нет встроенного стриминга

ТехнологияЧто этоПроблемы
WebSocketsПолно-дуплексная связьНе REST, отдельный протокол
SSE (Server-Sent Events)Server → Client стримингТолько односторонний, не REST
Long pollingКлиент ждёт ответаНеэффективно

gRPC: Встроенный стриминг

service LogService {
    rpc TailLogs (TailRequest) returns (stream LogEntry);  // server streaming
    rpc UploadLogs (stream LogEntry) returns (Summary);    // client streaming
    rpc Chat (stream ChatMessage) returns (stream ChatMessage); // bidirectional
}

Встроенная поддержка:

  • Server streaming (логи, метрики)
  • Client streaming (загрузка файлов)
  • Bidirectional streaming (чат, игры)

Кеширование

REST: Отличное кеширование

GET /users/123
Cache-Control: max-age=300
ETag: "abc123"
# При повторном запросе
GET /users/123
If-None-Match: "abc123"
→ 304 Not Modified (без тела)

Где кешируется: браузер, CDN, прокси.

gRPC: Нет встроенного кеширования

  • Все запросы — POST (даже для чтения)
  • HTTP кеш не работает
  • Нужен отдельный кеш (Redis, Memcached)

Браузеры

REST: Отличная поддержка

// Браузерный JavaScript
fetch('/api/users/123')
    .then(response => response.json())
    .then(user => console.log(user.name));

gRPC: Плохая поддержка

Браузеры не поддерживают gRPC напрямую. Нужен gRPC-Web — прослойка, которая эмулирует gRPC поверх HTTP/1.1.

// gRPC-Web
const client = new UserServiceClient('http://localhost:8080');
client.getUser({userId: 123}, (err, user) => {
    console.log(user.getName());
});

Ограничения gRPC-Web:

  • Нет bidirectional streaming
  • Нет client streaming
  • Дополнительная прослойка (Envoy, gRPC-Web proxy)

Производительность

Сравнение (условное)

ПараметрREST (JSON + HTTP/1.1)gRPC (protobuf + HTTP/2)
Размер запроса500 байт150 байт
Размер ответа5 КБ1.5 КБ
Время сериализации1 мс0.2 мс
Запросов в секунду10 00050 000
СтримингНетДа

Когда gRPC значительно быстрее

  • Много маленьких запросов (экономия на заголовках)
  • Высокая нагрузка (миллионы RPS)
  • Большие объёмы данных (компактный protobuf)

Когда разница незначительна

  • Несколько запросов в секунду
  • Маленькие объёмы данных
  • Внешнее API (узкое место — сеть, не сериализация)

Примеры кода

REST (клиент на Python)

import requests

response = requests.get('https://api.example.com/users/123')
user = response.json()
print(user['name'])

gRPC (клиент на Python)

import grpc
import user_pb2
import user_pb2_grpc

channel = grpc.insecure_channel('localhost:50051')
stub = user_pb2_grpc.UserServiceStub(channel)
request = user_pb2.GetUserRequest(user_id=123)
user = stub.GetUser(request)
print(user.name)

Когда использовать REST

СценарийПочему REST
Публичное APIПростота, документация, curl, браузер
Веб-приложенияБраузерная поддержка, кеширование
Внешние интеграцииПартнёрам проще использовать REST
CRUD операцииGET, POST, PUT, DELETE идеально подходят
Небольшие проектыМеньше сложности
Кеширование критичноHTTP кеш, CDN

Когда использовать gRPC

СценарийПочему gRPC
Внутренние микросервисыВысокая производительность, HTTP/2
Высокая нагрузкаМиллионы запросов в секунду
Потоковая передачаЛоги, метрики, чаты, загрузка файлов
Мультиязычные системыГенерация кода из .proto
Мобильные приложенияМеньше трафика, лучше батарея (но сложнее)
Строгая типизацияОшибки на этапе компиляции

Гибридный подход

Ничто не мешает использовать оба подхода вместе.

Архитектура: REST для внешних, gRPC для внутренних

    graph TD
    A[Внешний клиент] -->|REST / JSON| B[API Gateway]
    B -->|gRPC / protobuf| C[Сервис A]
    B -->|gRPC / protobuf| D[Сервис B]
    C -->|gRPC / protobuf| E[Сервис C]
  

Пример: REST + gRPC

  • Публичный API: REST (простота, кеширование, браузеры)
  • Внутренние сервисы: gRPC (скорость, стриминг, типизация)
  • API Gateway: Конвертирует REST → gRPC

Сравнительная таблица (расширенная)

АспектRESTgRPC
ТранспортHTTP/1.1, HTTP/2HTTP/2
ФорматJSON, XML, YAMLProtobuf (бинарный)
Человеко-читаемый✅ Да❌ Нет
Браузеры✅ Отлично❌ Плохо (gRPC-Web)
Кеширование✅ Отличное (HTTP, CDN)❌ Нет
Стриминг❌ Нет (только WebSockets)✅ Да (4 типа)
Мультиплексирование❌ Нет✅ Да
Типизация❌ Слабая✅ Строгая
Контракт⚠️ OpenAPI (опционально)✅ .proto (обязателен)
Генерация кода⚠️ Опционально✅ Да
Производительность⚠️ Средняя✅ Высокая
Размер сообщения⚠️ Большой✅ Маленький
Сложность✅ Низкая⚠️ Высокая
Публичные API✅ Отлично❌ Плохо
Микросервисы⚠️ Хорошо✅ Отлично
Мобильные приложения✅ Хорошо⚠️ Хорошо (но сложнее)

Распространённые ошибки

Ошибка 1: gRPC для публичного API

Вы сделали публичный API на gRPC. Внешние разработчики не могут его вызвать из браузера.

Исправление: REST для публичных API. gRPC для внутренних.

Ошибка 2: REST для высоконагруженных микросервисов

У вас 100 микросервисов, 1 млн запросов в секунду. REST с JSON и HTTP/1.1 не вывозит.

Исправление: gRPC для внутренних микросервисов.

Ошибка 3: gRPC для кешируемых данных

Вы делаете gRPC для каталога товаров, который обновляется раз в час. Нет кеширования.

Исправление: REST с CDN.

Ошибка 4: REST для стриминга

Вам нужен чат в реальном времени. Вы пытаетесь использовать REST + polling (каждую секунду запрос). Неэффективно.

Исправление: gRPC с bidirectional streaming.

Ошибка 5: REST без OpenAPI

У вас REST API, но нет OpenAPI. Клиенты не знают, как его вызывать.

Исправление: Документируйте REST с OpenAPI.

Резюме для системного аналитика

  1. REST и gRPC решают разные задачи. REST — для публичных API, внешних интеграций, веб-приложений. gRPC — для внутренних микросервисов, высоких нагрузок, потоковой передачи.

  2. Главные преимущества REST: простота, кеширование, браузеры, человеко-читаемый JSON.

  3. Главные преимущества gRPC: производительность (protobuf + HTTP/2), стриминг, строгая типизация, генерация кода.

  4. Форматы: JSON (REST) — большой, текстовый, медленный. Protobuf (gRPC) — маленький, бинарный, быстрый.

  5. Транспорт: HTTP/1.1 (REST) — одно соединение → один запрос. HTTP/2 (gRPC) — одно соединение → много потоков.

  6. Кеширование: REST отлично кешируется (HTTP, CDN). gRPC — нет (все запросы POST).

  7. Стриминг: REST не имеет встроенного стриминга (только WebSockets). gRPC имеет 4 типа стриминга.

Проверка знаний

Вопрос 1 из 4
Что обычно сильнее у gRPC по сравнению с REST?
Что обычно сильнее у REST по сравнению с gRPC?
Почему gRPC часто выбирают для микросервисов?
Какой тезис лучше всего описывает отношение gRPC и REST?

Вопросы, где были ошибки