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

Основные концепции

gRPC — высокопроизводительный фреймворк для удалённых вызовов от Google. Основан на Protocol Buffers (protobuf) — бинарной сериализации (компактнее и быстрее JSON в 2–3 раза) и HTTP/2 (мультиплексирование, сжатие заголовков). .proto файл — единый источник истины (контракт), описывает сообщения и сервисы, из него генерируется код на 10+ языках. Четыре типа вызовов: unary (1 запрос → 1 ответ), server streaming (1 → много), client streaming (много → 1), bidirectional streaming (много ↔ много). Преимущества: высокая производительность, строгая типизация, мультиязычность, потоковая передача. Недостатки: сложность, плохая поддержка браузеров (gRPC-Web ограничен), нечеловеко-читаемый формат, нет кеширования. Идеален для внутренних микросервисов, высокой нагрузки, потоковой передачи (логи, события, чаты). Не подходит для публичных API и внешних интеграций (REST проще).

Введение: Новый способ общения

Представьте, что два человека говорят на разных языках. Им нужен переводчик, который быстро, точно и эффективно передаёт смысл. Если они обмениваются короткими фразами, переводчик успевает. Но если они начинают говорить одновременно, перебивать друг друга, передавать длинные монологи — переводчик начинает спотыкаться. Время задержек растёт, теряются детали.

В мире API есть свои “языки”. REST говорит на HTTP/1.1 и JSON — медленно, но понятно для всех. GraphQL — на HTTP/1.1 и JSON — более гибко. Но есть API, которым нужна скорость. Нужны миллионы запросов в секунду. Нужна потоковая передача. Нужна строгая типизация.

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

Если REST — это “почтовое отделение” (вы отправляете письмо и ждёте ответа), то gRPC — это “телефонный разговор” (вы устанавливаете соединение и обмениваетесь сообщениями в реальном времени). gRPC создавался для внутренних микросервисных коммуникаций, где скорость, эффективность и строгие контракты критически важны.

Что такое gRPC

gRPC — это современный высокопроизводительный фреймворк для удалённого вызова процедур (RPC). Он позволяет вызывать метод на удалённом сервере так же просто, как локальную функцию.

// Определение сервиса
service Greeter {
    rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// Определение сообщений
message HelloRequest {
    string name = 1;
}

message HelloReply {
    string message = 1;
}

Ключевые характеристики:

ХарактеристикаОписание
Язык определения интерфейсаProtocol Buffers (protobuf)
ТранспортHTTP/2
СериализацияБинарная (protobuf)
МногопоточностьВстроенная поддержка
Потоковая передачаДа (односторонняя и двусторонняя)
Генерация кодаИз .proto файла
Мультиязычность10+ языков

Protocol Buffers (protobuf)

Что это такое

Protocol Buffers — это язык описания структур данных и механизм их бинарной сериализации. Это как JSON или XML, но быстрее и компактнее.

Определение сообщения

syntax = "proto3";

message Person {
    string name = 1;      // 1 — тег (не значение, а идентификатор поля)
    int32 age = 2;
    string email = 3;
    repeated string phone_numbers = 4;  // массив
    Address address = 5;
}

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

Типы данных

ТипОписаниеПример
stringСтрока UTF-8"Иван"
int32, int64Целые числа42
float, doubleЧисла с плавающей точкой3.14
boolЛогическое значениеtrue
bytesБинарные данныеизображение
enumПеречислениеStatus { ACTIVE=0; INACTIVE=1; }
repeatedМассивrepeated string tags
mapСловарьmap<string, string> metadata

Теги полей

Числа (1, 2, 3) — это теги, не значения. Они используются для идентификации полей в бинарном формате. Теги 1-15 занимают 1 байт, 16-2047 — 2 байта. Поэтому частые поля имеют маленькие теги.

Почему protobuf быстрее JSON

АспектJSONProtobuf
РазмерТекстовый, много лишних символов ({, }, ", :, ,)Бинарный, компактный
СериализацияМедленная (парсинг строк)Быстрая (чтение байтов)
ТипизацияСлабая (динамическая)Строгая (статическая)
ЧитаемостьЧеловеко-читаемыйМашино-читаемый (не для людей)

Пример сравнения:

// JSON — 80 байт
{
    "name": "Иван Петров",
    "age": 30,
    "email": "ivan@example.com"
}
// Protobuf — ~40 байт (в 2 раза меньше)
\x0a\x0bИван Петров\x10\x1e\x1a\x11ivan@example.com

.proto файл — единый источник истины

.proto файл — это контракт между клиентом и сервером. Он описывает:

  • Какие сообщения (структуры данных) существуют
  • Какие сервисы (RPC методы) доступны
syntax = "proto3";

package example.user;

// Определение сервиса
service UserService {
    rpc GetUser (GetUserRequest) returns (User);
    rpc CreateUser (CreateUserRequest) returns (User);
    rpc ListUsers (ListUsersRequest) returns (ListUsersResponse);
}

// Определения сообщений
message GetUserRequest {
    int32 user_id = 1;
}

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

message CreateUserRequest {
    string name = 1;
    string email = 2;
    int32 age = 3;
}

message ListUsersRequest {
    int32 limit = 1;
    int32 offset = 2;
}

message ListUsersResponse {
    repeated User users = 1;
    int32 total = 2;
}

Генерация кода

Из .proto файла генерируются клиентский и серверный код на нужном языке.

# Генерация для Go
protoc --go_out=. --go-grpc_out=. user.proto

# Генерация для Python
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. user.proto

# Генерация для Java
protoc --java_out=. --grpc-java_out=. user.proto

Что генерируется:

КомпонентНазначение
СообщенияКлассы для структур данных (Person, Address)
Клиент (stub)Клиентский код для вызова сервиса
Сервер (skeleton)Интерфейс, который нужно реализовать на сервере

Клиент-серверная архитектура gRPC

Сервер

// Go пример
type userServer struct {
    pb.UnimplementedUserServiceServer
}

func (s *userServer) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.User, error) {
    // Бизнес-логика
    return &pb.User{
        Id: req.UserId,
        Name: "Иван",
        Email: "ivan@example.com",
    }, nil
}

func main() {
    lis, _ := net.Listen("tcp", ":50051")
    grpcServer := grpc.NewServer()
    pb.RegisterUserServiceServer(grpcServer, &userServer{})
    grpcServer.Serve(lis)
}

Клиент

// Go пример
conn, _ := grpc.Dial("localhost:50051", grpc.WithInsecure())
client := pb.NewUserServiceClient(conn)

resp, err := client.GetUser(context.Background(), &pb.GetUserRequest{UserId: 123})
if err != nil {
    log.Fatal(err)
}
fmt.Println(resp.Name) // "Иван"

HTTP/2 как транспорт

gRPC использует HTTP/2, что даёт преимущества перед HTTP/1.1, который используют REST и GraphQL.

Преимущества HTTP/2

Особенность HTTP/2Что даёт gRPC
МультиплексированиеНесколько запросов по одному соединению (без head-of-line blocking)
Бинарный протоколКомпактнее и быстрее текстового
Сжатие заголовковМеньше трафика на повторяющиеся заголовки
Server PushСервер может отправлять данные без запроса (экспериментально в gRPC)

HTTP/2 vs HTTP/1.1

ХарактеристикаHTTP/1.1HTTP/2
СоединенияОдин запрос → одно соединениеОдно соединение → много запросов
ЗаголовкиТекстовые, без сжатияСжатые (HPACK)
ФорматТекстовыйБинарный
ПотокиНетДа (streams)
ПриоритетыНетДа

Четыре типа вызовов gRPC

(Подробно в следующей теме, здесь кратко)

ТипОписание
UnaryОдин запрос → один ответ (как REST)
Server streamingОдин запрос → поток ответов
Client streamingПоток запросов → один ответ
Bidirectional streamingПоток запросов → поток ответов

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

ПреимуществоОбъяснение
Высокая производительностьБинарная сериализация (protobuf), HTTP/2
Строгая типизацияКонтракт в .proto файле, генерация типизированного кода
МультиязычностьКлиенты и серверы на разных языках
Потоковая передачаВстроенная поддержка стримов
Автоматическая генерация кодаКлиент и сервер генерируются из .proto
Плаг-ин архитектураАутентификация, балансировка, мониторинг
Deadlines / TimeoutsВстроенная поддержка таймаутов
CancellationОтмена запросов
КаналыМультиплексирование запросов

Недостатки gRPC

НедостатокОбъяснение
Сложность настройкиНужно настраивать HTTP/2, протоколы
Ограниченная поддержка браузеровgRPC-Web (ограниченная функциональность)
Нечеловеко-читаемый форматБинарный protobuf сложно отлаживать
Крутая кривая обучения.proto, protobuf, HTTP/2, стримы
Нет встроенного кешированияНет аналога HTTP кеша
ИнструментыМеньше, чем для REST

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

Отличные сценарии

СценарийПочему gRPC подходит
МикросервисыВысокая производительность, строгие контракты
Внутренние APIНе нужно кеширование, браузеры
Высокая нагрузкаМиллионы запросов в секунду
Потоковая передачаЛоги, события, чаты
Мультиязычные системыКлиенты на Go, Java, Python, C++

Плохие сценарии

СценарийПочему gRPC не подходит
Публичные APIСложно использовать из браузера
Внешние интеграцииREST проще для партнёров
Небольшие проектыОверхед не оправдан
Кеширование важноНет встроенного кеширования

gRPC vs REST: Краткое сравнение

АспектRESTgRPC
ПротоколHTTP/1.1HTTP/2
ФорматJSON (текст)Protobuf (бинарный)
Размер сообщенияБольшойМаленький
СкоростьСредняяВысокая
ТипизацияСлабая (OpenAPI опционально)Строгая (.proto обязателен)
СтримингWebSockets или SSEВстроенная поддержка
БраузерыОтличноПлохо (gRPC-Web ограничен)
КешированиеОтличное (HTTP кеш)Нет
ЧитаемостьВысокая (curl)Низкая (бинарный)
СложностьНизкаяВысокая

Пример полного gRPC сервиса

.proto файл

syntax = "proto3";

package ecommerce;

service ProductService {
    rpc GetProduct (ProductRequest) returns (Product);
    rpc ListProducts (ListRequest) returns (stream Product);  // server streaming
    rpc CreateProducts (stream Product) returns (Summary);    // client streaming
    rpc Inventory (stream ProductRequest) returns (stream Stock); // bidirectional
}

message ProductRequest {
    int32 id = 1;
}

message Product {
    int32 id = 1;
    string name = 2;
    double price = 3;
    int32 stock = 4;
}

message ListRequest {
    int32 limit = 1;
    int32 offset = 2;
}

message Summary {
    int32 created_count = 1;
}

message Stock {
    int32 product_id = 1;
    int32 quantity = 2;
}

Вызовы из клиента

// Unary
product, _ := client.GetProduct(ctx, &ProductRequest{Id: 123})

// Server streaming
stream, _ := client.ListProducts(ctx, &ListRequest{Limit: 10})
for {
    product, err := stream.Recv()
    if err == io.EOF { break }
    fmt.Println(product)
}

// Client streaming
stream, _ := client.CreateProducts(ctx)
stream.Send(&Product{Name: "Товар 1", Price: 100})
stream.Send(&Product{Name: "Товар 2", Price: 200})
summary, _ := stream.CloseAndRecv()

// Bidirectional streaming
stream, _ := client.Inventory(ctx)
go func() {
    stream.Send(&ProductRequest{Id: 1})
    stream.Send(&ProductRequest{Id: 2})
    stream.CloseSend()
}()
for {
    stock, err := stream.Recv()
    if err == io.EOF { break }
    fmt.Println(stock)
}

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

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

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

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

Ошибка 2: Игнорирование Deadlines

// Плохо: без таймаута
client.GetUser(context.Background(), req)

// Хорошо: с таймаутом
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
client.GetUser(ctx, req)

Ошибка 3: Слишком большие сообщения

Protobuf не предназначен для сообщений > 100 МБ.

Исправление: Для больших файлов используйте отдельный сервис (например, gRPC для метаданных, HTTP для файлов).

Ошибка 4: Игнорирование ошибок потоков

// Плохо: не проверяем ошибки
for {
    msg, _ := stream.Recv()  // игнорируем ошибку
    process(msg)
}

Ошибка 5: gRPC для маленького проекта

Для трёх микросервисов с 10 запросами в секунду gRPC избыточен.

Исправление: Начинайте с REST, переходите на gRPC, когда вырастете.

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

  1. gRPC — высокопроизводительный фреймворк для удалённого вызова процедур от Google. Основан на Protocol Buffers (protobuf) и HTTP/2.

  2. Protocol Buffers — язык описания структур данных и механизм бинарной сериализации. Компактнее и быстрее JSON в 2-3 раза.

  3. .proto файл — единый источник истины. Описывает сообщения и сервисы. Из него генерируется код на 10+ языках.

  4. HTTP/2 даёт мультиплексирование (несколько запросов по одному соединению), сжатие заголовков, бинарный протокол.

  5. Четыре типа вызовов: unary (один-один), server streaming (один-много), client streaming (много-один), bidirectional streaming (много-много).

  6. Преимущества: высокая производительность, строгая типизация, мультиязычность, потоковая передача.

  7. Недостатки: сложность, плохая поддержка браузеров, нечеловеко-читаемый формат, нет кеширования.

  8. Идеальные сценарии: внутренние микросервисы, высокая нагрузка, потоковая передача, мультиязычные системы.

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

Вопрос 1 из 4
На чём строится контракт gRPC API?
Что особенно характерно для gRPC по сравнению с JSON API?
Зачем в gRPC важна генерация кода?
Какая идея лучше всего описывает gRPC как технологию?

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