Как сервер и клиент используют proto-файл в gRPC
Что проверяют
Понимаете ли вы, что proto-файл это контракт, из которого генерируется код для обеих сторон, и что на проводе важны номера полей, а не их имена. Следом обычно спрашивают, как менять контракт, не ломая уже работающих клиентов.
Как отвечать
В .proto описываются сообщения и сервисы с методами. Для Go из него генерируются два файла:
protoc-gen-go: файл*.pb.goсо структурами сообщений и кодом сериализации;protoc-gen-go-grpc: файл*_grpc.pb.goс интерфейсамиGreeterServerиGreeterClient, функциейRegisterGreeterServerи конструкторомNewGreeterClient.
Путь пакета задаёт option go_package. Генерацию запускают через protoc с флагами --go_out и --go-grpc_out или через buf generate. Buf заодно даёт buf lint и buf breaking, который ловит несовместимые изменения контракта.
Сервер реализует интерфейс: структура встраивает pb.UnimplementedGreeterServer по значению, методы принимают context.Context и сообщение. Дальше grpc.NewServer(), pb.RegisterGreeterServer(s, &server{}) и s.Serve(lis). Клиент открывает соединение через grpc.NewClient с grpc.WithTransportCredentials(...), получает pb.NewGreeterClient(conn) и вызывает методы как обычные функции Go. Под капотом HTTP/2, путь вида /example.Greeter/SayHello и бинарный protobuf.
Про эволюцию контракта: новые поля добавлять можно, старые клиенты пропустят их как неизвестные. Номер поля менять нельзя, удалённые номера и имена закрывают через reserved. Ошибки возвращают через status.Error(codes.NotFound, ...), а не текстом в теле ответа.
Где спотыкаются
Используют grpc.Dial и grpc.WithInsecure() из старых примеров. Оба помечены устаревшими, для локальной разработки есть insecure.NewCredentials().
Думают, что сервер передаёт схему клиенту. Схема вшита в сгенерированный код с обеих сторон, и разошедшиеся версии proto дают тихо потерянные или неверно прочитанные поля. Посмотреть схему на лету можно только при включённом reflection, например через grpcurl.
Не различают нулевое значение и отсутствие поля. В proto3 пустая строка и незаданное поле неотличимы, пока поле не помечено optional или не обёрнуто в wrapper-тип.