gRPC-GoMiddleExperience
В каких проектах gRPC-Go оправдан, а где он становится источником лишней сложности?
gRPC-Go оправдан в высоконагруженных внутренних микросервисах с чётким контрактом API; избыточен для простых CRUD-сервисов, публичных REST API или небольших монолитов без явной необходимости в стриминге.
Когда gRPC-Go оправдан
gRPC-Go хорошо вписывается в следующие сценарии:
- Внутренние микросервисы с высокой нагрузкой: Protobuf быстрее JSON в 3–10 раз, HTTP/2 снижает задержки при fan-out вызовах. Типичный пример — payment gateway, real-time recommendation engine, ML inference pipeline.
- Чёткий контракт между командами:
.proto-файлы — это живая документация;protoc-gen-goгенерирует типизированный клиент, который сложно использовать неправильно. - Стриминговые данные: серверный стриминг для live-feeds, двунаправленный — для чатов и телеметрии. Реализовать это поверх REST/JSON значительно сложнее.
- Полиглотная архитектура: gRPC поддерживает Go, Python, Java, Rust, Node.js из одного .proto; JSON REST API требует отдельной генерации или ручных клиентов.
- Дедлайны и отмена: механизм propagation контекста и
grpc-timeoutзаголовок работают из коробки — критично для SLA-чувствительных систем.
Когда gRPC-Go создаёт лишнюю сложность
- Публичный API для браузеров: браузеры не поддерживают gRPC напрямую. Нужен
grpc-gatewayилиconnect-go, что добавляет слой. Если клиент только браузер — REST проще. - Небольшой монолит или 2–3 сервиса: overhead от code generation, protoc toolchain и необходимости версионировать .proto не окупается при малом числе вызывающих сторон.
- CRUD без сложной логики: для простого
GET /users/{id}gRPC добавляет слой абстракции без выигрыша в производительности при разумной нагрузке. - Команда без опыта с Protobuf: кривая обучения включает настройку
buf.buildилиprotoc, понимание wire format, управлениеreservedполями и backward compatibility.
Конкретное сравнение
// REST: любой HTTP-клиент, curl, браузер — без инфраструктуры
resp, _ := http.Get("https://api.example.com/users/42")
// gRPC-Go: нужен сгенерированный клиент, TLS, правильный port
conn, _ := grpc.NewClient("api.example.com:443",
grpc.WithTransportCredentials(credentials.NewTLS(nil)))
client := pb.NewUserServiceClient(conn)
resp, _ := client.GetUser(ctx, &pb.GetUserRequest{Id: "42"})
REST быстрее в разработке прототипа, gRPC даёт типобезопасность и производительность в production.
Альтернативы при частичных требованиях
connect-go(bufbuild) — gRPC-совместимый протокол, но работает через обычный HTTP/1.1+JSON, доступен из браузера без прокси.twirp— Protobuf поверх HTTP/1.1, проще в отладке, но без стриминга.- OpenAPI +
oapi-codegen— типизированный REST с code gen, знакомый экосистеме.
Подводные камни
- Добавить gRPC «потому что модно» без реального fan-out или стриминга — команда платит за сложность, не получая выигрыша в latency.
- Не заложить versioning стратегию с самого начала — смена номеров полей в .proto ломает бинарную совместимость без явного
reserved. - Использовать gRPC для публичного API без grpc-gateway — браузерные клиенты требуют дополнительного слоя или Envoy с
grpc_json_transcoder. - Недооценить operational cost: grpcurl, wireshark, логирование payload — всё сложнее, чем с JSON REST.
- Хранить .proto только в одном репозитории без shared registry (buf BSR) — при нескольких командах возникают конфликты версий.
- Не настроить health-check через
grpc_health_v1— стандартные load-balancer health-check endpoints не работают с gRPC из коробки. - Полагаться на встроенный DNS-балансировщик gRPC без понимания ограничений — при Kubernetes service DNS резолвится в один IP, а не список pod'ов.
What hurts your answer
- Выбирать gRPC-Go по популярности, а не по требованиям проекта
- Игнорировать опыт команды, эксплуатацию и стоимость поддержки
- Не называть ситуации, где gRPC-Go будет плохим выбором
What they're listening for
- Называет критерии выбора gRPC-Go
- Учитывает команду, эксплуатацию, стоимость и риски
- Может назвать сценарии, где выбрал бы альтернативу