gRPC-GoJuniorCoding
Как реализовать gRPC-клиент на Go?
gRPC-клиент в Go создаётся через grpc.NewClient(), которому передаётся адрес и опции соединения. Затем из соединения создаётся сгенерированный stub и вызываются методы через context.
Пошаговая реализация gRPC-клиента на Go
Шаг 1 — .proto и кодогенерация
syntax = "proto3";
package user;
option go_package = "example.com/gen/user";
service UserService {
rpc GetUser(GetUserRequest) returns (GetUserResponse);
rpc ListUsers(ListUsersRequest) returns (stream User);
}
message GetUserRequest { string id = 1; }
message GetUserResponse { User user = 1; }
message ListUsersRequest{}
message User { string id = 1; string name = 2; }
protoc --go_out=. --go-grpc_out=. user.proto
Шаг 2 — установка зависимостей
go get google.golang.org/grpc@latest
go get google.golang.org/protobuf@latest
Шаг 3 — код клиента
package main
import (
"context"
"io"
"log"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
pb "example.com/gen/user"
)
func main() {
// NewClient не открывает соединение сразу — оно lazy
conn, err := grpc.NewClient(
"localhost:50051",
grpc.WithTransportCredentials(insecure.NewCredentials()),
)
if err != nil {
log.Fatalf("dial: %v", err)
}
defer conn.Close()
client := pb.NewUserServiceClient(conn)
// Unary call с таймаутом
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
resp, err := client.GetUser(ctx, &pb.GetUserRequest{Id: "42"})
if err != nil {
log.Fatalf("GetUser: %v", err)
}
log.Printf("user: %v", resp.User)
// Server streaming call
stream, err := client.ListUsers(ctx, &pb.ListUsersRequest{})
if err != nil {
log.Fatalf("ListUsers: %v", err)
}
for {
user, err := stream.Recv()
if err == io.EOF {
break
}
if err != nil {
log.Fatalf("stream.Recv: %v", err)
}
log.Printf("received: %s", user.Name)
}
}
TLS-соединение
import "google.golang.org/grpc/credentials"
creds, err := credentials.NewClientTLSFromFile("ca.crt", "")
conn, err := grpc.NewClient("prod.example.com:443",
grpc.WithTransportCredentials(creds),
)
Добавление метаданных (токен аутентификации)
import "google.golang.org/grpc/metadata"
ctx = metadata.AppendToOutgoingContext(ctx, "authorization", "Bearer "+token)
resp, err := client.GetUser(ctx, req)
Подводные камни
grpc.NewClient()возвращает соединение немедленно, даже если сервер недоступен — первая реальная ошибка возникнет при RPC-вызове.- Не забывайте
conn.Close()через defer; утечка соединений приводит к исчерпанию дескрипторов файлов. - Всегда устанавливайте таймаут через
context.WithTimeout; без него клиент будет ждать бесконечно при зависшем сервере. - Для production необходимо
credentialsвместоinsecure.NewCredentials(); insecure допустим только внутри доверенной сети. - По умолчанию gRPC-клиент выбирает один адрес из DNS; для балансировки нагрузки нужно явно установить
grpc.WithDefaultServiceConfigс политикойround_robin. - Метаданные нужно добавлять к каждому вызову отдельно или через
UnaryClientInterceptor— они не «прилипают» к conn. - При отмене контекста
stream.Recv()вернёт ошибку с кодомcodes.Canceled, а неio.EOF— обрабатывайте оба случая.
Common mistakes
- Давать ответ про реализация gRPC-клиента на Go только на уровне определения, не показывая поведение в реальном приложении.
- Игнорировать границы ответственности вокруг темы «реализация gRPC-клиента на Go»: кто отменяет работу, кто владеет ресурсом и где формируется ответ клиенту.
- Не связывать реализация gRPC-клиента на Go с observability, тестированием или безопасностью, когда это влияет на продакшен-поведение.
What the interviewer is testing
- Точно объясняет, что именно делает реализация gRPC-клиента на Go и где это используется в Go-коде.
- Связывает реализация gRPC-клиента на Go с корректным lifecycle запроса, отменой, конкурентностью или конфигурацией сервера там, где это уместно.
- Не изобретает API и опирается на реальные контракты официальной документации.