Cлушай, если ты до сих пор шлёшь JSON туда-��юда и мучаешься с валидацией — пора смотреть в сторону gRPC. Но сперва давай без пафоса разберём, что это вообще такое.
Что за зверь gRPC и почему не JSON?
JSON (наш старый друг):
// Ты шлёшь это на сервер
POST /api/users
{"name": "Вася", "email": "vasya@mail.ru"}
// Сервер отвечает тебе
{"id": 1, "name": "Вася", "email": "vasya@mail.ru", "error": null}
Проблемы:
Ты тратишь байты на кавычки и названия полей
Нет схемы — можно опечататься в
emialвместоemailПриложение месяц работает, потом падает
Каждый раз парсим строку в объект (дорого)
gRPC (новый подход):
// Сначала определяем КОНТРАКТ (схему)
message User {
string name = 1;
string email = 2;
}
// И говорим, какие методы есть
service UserService {
rpc CreateUser(User) returns (User);
}
Суть:
Есть
.protoфайл — единый контракт для всехИз него генерится код под 10+ языков
Данные передаются в бинарном формате (protobuf)
HTTP/2 под капотом (можно много запросов в одном соединении)
Что вообще такое gRPC простыми словами?
Представь, что JSON — это бумажные документы. Ты заполняешь бланк, отдаёшь секретарше, она бежит в другой отдел, там его заполняют и возвращают.
gRPC — это внутренний телефон. У тебя есть список контактов (сервисов), ты звонишь конкретному человеку (методу) и говоришь чётко по инструкции. Все инструкции записаны в единой базе.
Технические отличия (без воды)
Параметр |
JSON (REST) |
gRPC |
|---|---|---|
Формат данных |
Текст (JSON) |
Бинарный (Protocol Buffers) |
Схема |
Нет (Swagger опционально) |
Обязательная (.proto файл) |
Скорость |
1x (база) |
5-10x быстрее |
Размер данных |
1x (база) |
2-10x меньше |
Потоковость |
Только запрос-ответ |
Потоки в обе стороны |
Клиенты |
Пишешь сам/используешь SDK |
Автогенерация из .proto |
Самый главный кейс, где gRPC рвёт
Микросервисы внутри одного продукта. Допустим:
auth-service(аутентификация)payment-service(платежи)notification-service(уведомления)
Они общаются между собой. С JSON у тебя:
Каждый пишет свой клиент к другому
При изменении API ломается всё
Нужно следить за версиями
С gRPC:
Общий
proto/каталог с контрактамиИзменяешь
.proto→ все сервисы не скомпилируются, пока не обновятсяЕдиная точка правды
Показываю на пальцах
1. Ставим движок (Ubuntu/Debian):
sudo apt install protobuf-compiler
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
2. Контракт (api/auth.proto):
syntax = "proto3";
package api;
option go_package = ".;api";
// Вот она, схема. Единая для всех
message LoginRequest {
string username = 1;
string password = 2;
}
message LoginResponse {
string token = 1;
int32 user_id = 2;
}
// Сервис — это набор методов
service AuthService {
rpc Login(LoginRequest) returns (LoginResponse);
}
3. Генерим код (магия):
protoc --go_out=. --go-grpc_out=. api/auth.proto
Появятся auth.pb.go и auth_grpc.pb.go. Не трогай их руками! Это сгенерированный код.
4. Сервер (server.go):
ackage main
import (
"context"
"log"
"net"
"google.golang.org/grpc"
pb "ваш_проект/api"
)
type server struct {
pb.UnimplementedAuthServiceServer
}
func (s *server) Login(ctx context.Context, req *pb.LoginRequest) (*pb.LoginResponse, error) {
// Здесь твоя бизнес-логика
if req.Username == "admin" && req.Password == "123" {
return &pb.LoginResponse{
Token: "jwt_token_here",
UserId: 1,
}, nil
}
return nil, errors.New("invalid credentials")
}
func main() {
lis, _ := net.Listen("tcp", ":50051")
s := grpc.NewServer()
pb.RegisterAuthServiceServer(s, &server{})
log.Println("Server started on :50051")
log.Fatal(s.Serve(lis))
}
5. Клиент (client.go):
package main
import (
"context"
"log"
"google.golang.org/grpc"
pb "ваш_проект/api"
)
func main() {
conn, _ := grpc.Dial("localhost:50051", grpc.WithInsecure())
defer conn.Close()
client := pb.NewAuthServiceClient(conn)
// Смотри, какая красота! Типизированный вызов
resp, err := client.Login(context.Background(), &pb.LoginRequest{
Username: "admin",
Password: "123",
})
if err != nil {
log.Fatal(err)
}
log.Printf("Token: %s, UserID: %d", resp.Token, resp.UserId)
}
Запускаем
# Терминал 1
go run server.go
# Терминал 2
go run client.go
# Вывод: Token: jwt_token_here, UserID: 1
Когда НЕ НАДО использовать gRPC?
Публичное API для неизвестных клиентов (браузеры, мобилки с старыми либами)
Простой CRUD с 3 методами и 2 пользователями
Нет контроля над клиентом (отдаёшь API сторонним разработчикам)
А если нужен и REST тоже?
Есть grpc-gateway — прокси, который преобразует REST в gRPC. Пишешь аннотации в .proto:
import "google/api/annotations.proto";
service AuthService {
rpc Login(LoginRequest) returns (LoginResponse) {
option (google.api.http) = {
post: "/v1/login"
body: "*"
};
}
}
И получаешь REST-эндпоинт автоматически.
Итог
Берём gRPC, если:
Микросервисы
Нужна скорость (игровые сервера, трейдинг, стриминг)
Клиенты на разных языках
Хочется типизации и автогенерации
Остаёмся с JSON, если:
Отдаём API наружу
Простой проект
Клиенты только браузеры
Нет времени разбираться с новой технологией
Спасибо за внимание :)
Комментарии (22)

kain64b
03.01.2026 20:06Смешались люди, кони..
Protobuf корректно сравнивать с json, либо rest/graphql/rpc/etc с grpc. То есть в принципе не корректное название и статья :(
Grpc выглядит хорошо, пока не начинается DEADLINE_EXCEEDED )

Pusk1
03.01.2026 20:06Есть
.protoфайл — единый контракт для всех - Open APIИз него генерится код под 10+ языков - Open API
Данные передаются в бинарном формате (protobuf) +
HTTP/2 под капотом (можно много запросов в одном соединении) - доступно с REST API и должно использоваться по умолчанию. HTTP/3 для мобилки заходит ещё лучше, т.к. нормально переживает потери пакетов, но есть риски с роскомнадзором.

stranger_shaman
03.01.2026 20:06абсолютно все Ваши пункты верны для любого другого подхода с ватогенерацией классов. Упоминать Http2 это вообще смешно.

Lomserman
03.01.2026 20:06Хочу п2 прокомментировать.
Хз как под другие языки, но под Python там код генерится максимально отвратительный. И я не про форматирование (его там нет) и не про скорость работы кода (не замерял), а именно про удобство обращения к этому сгенерированному слопу из своего кода. Там на выходе реально нечитаемая кодомасса с отрицательной типизацией, с которой без оглядки на сырые прото-файлы работать просто нереально.

lesskop
03.01.2026 20:06Когда в LLM выбираешь профессиональный тон, почти всегда в её ответы проникают фразы из статьи:
давай без пафоса разберём
Технические отличия (без воды)
Показываю на пальцах

Так выглядит в ChatGPT Рецепт статьи на Хабр:
Берём нейрослоп
Добавляем в конец: "Спасибо за внимание :)"
Спасибо, что хоть без рекламы ТГ

MrEx3cut0r Автор
03.01.2026 20:06Не скрываю)
Вообще не было времени, так что пришлось использовать ИИ, а статью написать чет хотелось

dimuska139
03.01.2026 20:06gRPC против JSON
Ручка против перпендикуляра
Нет схемы — можно опечататься в
emialвместоemailОна есть - OpenAPI/Swagger. И кодогенерация из неё есть. Другое дело, что, видимо, вы её почему-то решили не использовать.
Приложение месяц работает, потом падает
Вообще никакой связи нет между форматом API и падением вашего приложения. Проблема где-то в другом месте.
С JSON у тебя ... Нужно следить за версиями
То есть в случае с gRPC мне не нужно следить за обратной совместимостью? Как бы не так
Есть
.protoфайл — единый контракт для всех... Из него генерится код под 10+ языковТо же самое можно (и нужно) делать и в случае REST-API: пишем файл со спецификацией и из него генерируем код под 10+ языков.
Берём gRPC, если ... Микросервисы
Вообще нет связи
Берём gRPC, если ... Клиенты на разных языках
Тоже мимо, т.к. это есть и без gRPC
Берём gRPC, если ... Хочется типизации и автогенерации
Аналогично

savostin
03.01.2026 20:06*чисто потрындеть
А знает ли кто зачем и почему нужны эти циферки в proto?
message LoginRequest { string username =1;string password =2;}Порядок-то полей чем не угодил? Или "это другое"?

ermadmi78
03.01.2026 20:06Это идентификаторы полей. Во время сериализации сообщения в буфер пишется не имя поля, а его идентификатор. Во время десериализации имя поля ищется в схеме по его идентификатору. Такой подход позволяет весьма и весьма существенно сократить размер сериализованного сообщения по сравнению например с тем же JSON.
Идентификатор поля в схеме прописывается явно чтобы обеспечить бинарную совместимость сообщений разных версий схемы во время десериализации.

Heggi
03.01.2026 20:06И, благодаря цифровым идентификаторам, буквенные названия полей перестают играть свою роль в передачи сообщения. Т.е. в новой версии proto-файла можно переименовать поле и это ничего не сломает. Можно переименовать даже название самого message, это тоже ничего не сломает (но естественно, что эти переименования нужно будет учесть в самом коде после обновления proto-файла на актуальную версию).
Нельзя переименовывать только названия методов, т.к. они напрямую связаны с path запроса.

savostin
03.01.2026 20:06Это понятно. Циферки зачем? Порядок чем не устроил? Если для "обратной совместимости", то идентификаторами можно так же легко сломать, как и порядком. Т.е. на совести разработчика поддерживать эту совместимость и формат никаких ограничений не накладывает.

Heggi
03.01.2026 20:06Порядок как раз вреден. В сообщении поля удобнее группировать по назначению. К примеру передаешь адрес, поля: город, улица, дом, квартира. Кроме этих полей еще еще пачка других (фио, телефон и т.п.). Завтра бизнес пришел и сказал: нужно добавить почтовый индекс. Логичнее почтовый индекс разместить перед полем город или после поля квартира. Но это сломает тот самый порядок, т.к. новое поле вклинится между другими.
Или наоборот, убираем какое-то поле, т.к. оно больше не используется. Опять же ломается порядок.

ermadmi78
03.01.2026 20:06Ну а если вернуться к теме статьи, то JSON нужно сравнивать не с gRPC, а с Google Protocol Buffers. Так как и то и другое является протоколом сериализации данных.
Главное преимущество Protocol Buffers - это компактный размер сериализованного сообщения за счёт бинарного формата сериализации и за счёт индексации имён полей в схеме. Дополнительное преимущество - само наличие схемы, и инструменты кодогенерации, что позволяет вести разработку в парадигме Schema First.
Но, главное преимущество Protocol Buffers - бинарный формат ‐ является и главным его недостатком. JSON сериализует сообщение в строку, в человекочитаемом виде, что упрощает разработку, отладку и поддержку сервисов, которые общаются между собой с помощью JSON сообщений. Вам не нужны дополнительные инструменты, чтобы прочесть сериализованное сообщение. А бинарный формат Protocol Buffers "глазами" не прочтёшь.
Поэтому если у нас злой хайлоад с жёстким SLA - берём Protocol Buffers в качестве протокола сериализации. Если у нас обычный проект со среднестатистической нагрузкой - берём JSON в качестве протокола сериализации, и упрощаем себе разработку, отладку и поддержку. На практике Protocol Buffers имеет смысл использовать от силы в 2% проектов на рынке, так как оставшиеся 98% проектов до Google по нагрузке, мягко говоря, не дотягивают.
Если говорить о парадигме Schema First при коммуникации с помощью JSON то тут стандартом является OpenAPI/Swagger. Но я его, честно говоря, не долюбливаю, так как это скорее имитация схемы а не полноценная схема из за нестрогого синтаксиса. Никто не мешает вам описать часть сообщения, вместо всего сообщения. Или вообще ошибиться в описании. Лично я при работе с JSON предпочитаю использовать GraphQL в качестве средства описания схемы. В качестве инструмента кодогенерации для GraphQL я использую Kobby.

Arm79
03.01.2026 20:06Как можно сравнивать протокол взаимодействия grps и формат представления данных json? С каких пор у json нет схем? Компактность protobuf сильно преувеличена, особенно если передаются строки. Json + gzip тоже достаточно компактное представление. Использование общего каталога схем с запретом компиляции сервисов до перехода на обновленную версию контракта - полностью ставит крест на самой идее микросервисов, вынуждая каждый раз тратить ресурсы разных команд на поддержание перехода, тем самым делая каждый релиз монструозным и неповоротливым.

ermadmi78
03.01.2026 20:06На практике gRPC вполне подходит для микросервисной архитектуры. Никто не мешает вам оперировать несколькими версиями схемы. И, при правильном проектировании protobuf обеспечивает вам бинарную совместимость сообщений в разных версиях схемы. Та же самая история и с GraphQL, там тоже не проблема работать с несколькими версиями схемы.

CatAssa
03.01.2026 20:06JSON vs gRPC.
Тестовый формат обмена данными vs платформа взаимодейсвия программных компонентов.

Maliglood
03.01.2026 20:06На чём вы пишите, что вам вручную приходится каждый раз вводить названия полей, из-за чего вы можете опечататься? Лет 10 использую JSON в .NET - не помню такой проблемы.
"Приложение месяц работает, потом падает" - прям вот JSON положил код???
"Единый контракт для всех" - если у вас микросервисы, то создавать единый репозиторий, от которого зависят все микросервисы, - это плохое решение. По-хорошему, каждый микросервис должен иметь свой репозиторий со своими контрактами - а чем это отличается от генерации контрактов с OpenAPI?
DigiHun
Чем openapi контракты не угодили? Есть же генераторы, которые под разные языки пишут код, при чем можно самостоятельно докрутить, так как все опенсорсное.
Единственный плюс, который отличает от REST, в использовании http 2 версии и всего сопутствующего
MrEx3cut0r Автор
В статье я не говорил(наверное) что openapi контракты хуже, просто выделил хорошие и плохие стороны каждого, и по каким критериям подбирать.
stranger_shaman
grpc это новый подход? серьезно?
mayorovp
Хреновые там генераторы, сколько не смотрел разных - что-нибудь да криво.
Плюс openapi просто не работает с событиями, идущими с бэка на фронт независимо от запросов (SSE, веб-сокеты, вот это всё)