gRPC в Go
gRPC — RPC-фреймворк от Google поверх HTTP/2 и Protocol Buffers. Контракт описывают в .proto, код генерируют protoc и плагины Go. Статья дополняет обзор REST, GraphQL и gRPC и справочник по gRPC.
См. также: Веб на stdlib · Gin · Дженерики · Асинхронность.
gRPC и REST — когда что выбирать
| Критерий | gRPC | REST (JSON) |
|---|---|---|
| Формат | Бинарный Protobuf | Текст JSON |
| Транспорт | HTTP/2, multiplexing | HTTP/1.1 или HTTP/2 |
| Контракт | .proto, строгая схема | OpenAPI, часто "по факту" |
| Браузер | Нужен grpc-web / proxy | Нативно |
| Стриминг | Unary, server/client/bidi stream | SSE, WebSocket отдельно |
| Типичное применение | Микросервис ↔ микросервис | Публичное API, BFF |
В Go один процесс часто поднимает REST для внешних клиентов и gRPC для внутренних вызовов — см. пример микросервиса.
Инструменты
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
Разбор:
protoc(компилятор protobuf) ставят отдельно с protobuf.dev.protoc-gen-goгенерирует Go-структуры сообщений.protoc-gen-go-grpcгенерирует интерфейсы сервиса и регистрацию в gRPC.
В go.mod обычно появляются google.golang.org/grpc и google.golang.org/protobuf.
Файл .proto
Минимальный контракт телефонной книги (упрощённый учебный пример):
Код ITЗагрузка примера кода…
Разбор:
proto3— актуальная версия синтаксиса; поля нумеруются и не меняют номер после релиза.go_packageзадаёт import path и имя Go-пакета (pb).service PhoneBookобъявляет RPC-методGetс типами запроса и ответа.
Генерация:
protoc --go_out=. --go-grpc_out=. \
--go_opt=paths=source_relative \
--go-grpc_opt=paths=source_relative \
phonebook/v1/phonebook.proto
Разбор:
paths=source_relativeкладёт.pb.goрядом с.proto— удобно в монорепо.- В репозитории часто коммитят сгенерированный код или генерируют в CI через
go:generate.
Сервер
Код ITЗагрузка примера кода…
Разбор:
- Встраивание
UnimplementedPhoneBookServer— forward compatibility: новые методы в.protoне ломают старый бинарник до реализации. status.Errorf(codes.NotFound, ...)переводит доменную ошибку в gRPC-код; клиент читаетstatus.Code(err).- Порт
:50051— распространённый convention для gRPC (не стандарт, а привычка).
Клиент
conn, err := grpc.Dial("localhost:50051",
grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatal(err)
}
defer conn.Close()
client := pb.NewPhoneBookClient(conn)
resp, err := client.Get(context.Background(), &pb.GetRequest{Name: "Ann"})
if err != nil {
log.Fatal(err)
}
fmt.Println(resp.GetContact().GetPhone())
Разбор:
grpc.Dialсоздаёт долгоживущее HTTP/2-соединение; клиент переиспользует его для многих RPC.insecure.NewCredentials()— только для локальной разработки; в проде — TLS (credentials.NewClientTLSFromFileили mTLS).- Первый аргумент RPC — всегда
context.Contextдля таймаутов и отмены — см. асинхронность.
В production включайте TLS или mTLS между сервисами. Незашифрованный gRPC допустим лишь в доверенной сети или на localhost.
Streaming
gRPC поддерживает четыре вида вызовов:
| Вид | Направление |
|---|---|
| Unary | один запрос — один ответ |
| Server streaming | клиент шлёт один запрос, сервер — поток ответов |
| Client streaming | клиент шлёт поток, сервер — один ответ |
| Bidirectional | оба потока одновременно |
В .proto это задаёт ключевое слово stream у аргумента или возвращаемого типа. На сервере реализуют Send/Recv на stream-объекте; на клиенте — аналогично. Для больших выборок из БД server streaming снижает задержку первого байта по сравнению с одним огромным JSON.
Ошибки и метаданные
- Коды:
codes.NotFound,InvalidArgument,DeadlineExceeded,Unavailable. - Проверка на клиенте:
st, ok := status.FromError(err)
if ok && st.Code() == codes.NotFound {
// ...
}
- Metadata — аналог HTTP-заголовков (trace-id, auth token); передаётся через
metadata.NewOutgoingContext.
Тестирование
- Unit: мок интерфейса
PhoneBookServerили тестовый in-memory сервер. - Интegration:
bufconn/ локальный listener без сети:
lis := bufconn.Listen(1024 * 1024)
// Register server on lis, dial через grpc.WithContextDialer
- gRPC совместим с table-driven тестами; для HTTP-gateway поверх gRPC используют grpc-gateway (отдельная тема).
Связка с REST
Паттерн gRPC + grpc-gateway: один .proto, gRPC внутри кластера, JSON REST снаружи. Альтернатива — два контракта (OpenAPI + proto) с риском рассинхронизации.
Типичный путь сервиса контактов: CLI → веб на stdlib → REST на Gin → gRPC (эта статья).
Ключевые тезисы
- Контракт в
.proto— источник правды для сообщений и RPC. - Сервер и клиент генерируются; ручной JSON не нужен между Go-сервисами.
context, кодыstatusи TLS — обязательная часть production-разработки.- Для публичного API чаще оставляют REST; gRPC — для низкой задержки и строгих контрактов между сервисами.
Мини-практикум
- Опишите
.protoс методамиList(server stream) иAdd(unary). - Поднимите сервер и клиент на localhost; добавьте
context.WithTimeoutна вызов. - Сравните размер payload Protobuf и JSON для одной структуры
Contact.
Типичные ошибки
| Ошибка | Что делать |
|---|---|
Менять номера полей в .proto | Только добавлять поля с новыми номерами |
Забыть Unimplemented*Server | Встроить заглушку из generated-кода |
grpc.Dial без TLS в проде | Настроить credentials |
Игнорировать ctx на сервере | Проверять ctx.Done() в долгих операциях |
Связанные материалы
- Справочник по gRPC
- REST, GraphQL и gRPC — обзор
- Следующий шаг — профилирование и fuzz
Базовый разбор HTTP и HTTPS — HTTP как основа веб-интеграций.