Основные концепции
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
| Аспект | JSON | Protobuf |
|---|---|---|
| Размер | Текстовый, много лишних символов ({, }, ", :, ,) | Бинарный, компактный |
| Сериализация | Медленная (парсинг строк) | Быстрая (чтение байтов) |
| Типизация | Слабая (динамическая) | Строгая (статическая) |
| Читаемость | Человеко-читаемый | Машино-читаемый (не для людей) |
Пример сравнения:
// 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.1 | HTTP/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: Краткое сравнение
| Аспект | REST | gRPC |
|---|---|---|
| Протокол | HTTP/1.1 | HTTP/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, когда вырастете.
Резюме для системного аналитика
gRPC — высокопроизводительный фреймворк для удалённого вызова процедур от Google. Основан на Protocol Buffers (protobuf) и HTTP/2.
Protocol Buffers — язык описания структур данных и механизм бинарной сериализации. Компактнее и быстрее JSON в 2-3 раза.
.proto файл — единый источник истины. Описывает сообщения и сервисы. Из него генерируется код на 10+ языках.
HTTP/2 даёт мультиплексирование (несколько запросов по одному соединению), сжатие заголовков, бинарный протокол.
Четыре типа вызовов: unary (один-один), server streaming (один-много), client streaming (много-один), bidirectional streaming (много-много).
Преимущества: высокая производительность, строгая типизация, мультиязычность, потоковая передача.
Недостатки: сложность, плохая поддержка браузеров, нечеловеко-читаемый формат, нет кеширования.
Идеальные сценарии: внутренние микросервисы, высокая нагрузка, потоковая передача, мультиязычные системы.