Что такое gRPC
gRPC — это высокопроизводительный RPC-фреймворк от Google. Он использует Protocol Buffers для сериализации и HTTP/2 для транспорта.
▸gRPC vs REST
Определение сервиса
▸user.proto
1syntax = "proto3";23package user;45option go_package = "github.com/user/proto;userpb";67service UserService {8 rpc GetUser(GetUserRequest) returns (User);9 rpc ListUsers(ListUsersRequest) returns (stream User);10 rpc CreateUser(CreateUserRequest) returns (User);11}1213message User {14 int32 id = 1;15 string name = 2;16 string email = 3;17}1819message GetUserRequest {20 int32 id = 1;21}2223message ListUsersRequest {24 int32 page = 1;25 int32 per_page = 2;26}2728message CreateUserRequest {29 string name = 1;30 string email = 2;31}
▸Генерация кода
1# Установка protoc2brew install protobuf34# Установка плагинов5go install google.golang.org/protobuf/cmd/protoc-gen-go@latest6go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest78# Генерация9protoc --go_out=. --go-grpc_out=. user.proto
Серверная часть
1package main23import (4 "context"5 "log"6 "net"78 "google.golang.org/grpc"9 "github.com/user/proto/userpb"10)1112type server struct {13 userpb.UnimplementedUserServiceServer14}1516func (s *server) GetUser(ctx context.Context, req *userpb.GetUserRequest) (*userpb.User, error) {17 return &userpb.User{18 Id: req.Id,19 Name: "Alice",20 Email: "alice@example.com",21 }, nil22}2324func (s *server) ListUsers(req *userpb.ListUsersRequest, stream userpb.UserService_ListUsersServer) error {25 for i := int32(0); i < 10; i++ {26 if err := stream.Send(&userpb.User{27 Id: i,28 Name: "User",29 Email: "user@example.com",30 }); err != nil {31 return err32 }33 }34 return nil35}3637func main() {38 lis, _ := net.Listen("tcp", ":50051")39 grpcServer := grpc.NewServer()40 userpb.RegisterUserServiceServer(grpcServer, &server{})4142 log.Println("gRPC server on :50051")43 grpcServer.Serve(lis)44}
Клиентская часть
1package main23import (4 "context"5 "log"67 "google.golang.org/grpc"8 "google.golang.org/grpc/credentials/insecure"9 "github.com/user/proto/userpb"10)1112func main() {13 conn, _ := grpc.Dial("localhost:50051",14 grpc.WithTransportCredentials(insecure.NewCredentials()))15 defer conn.Close()1617 client := userpb.NewUserServiceClient(conn)1819 // Unary RPC20 user, err := client.GetUser(context.Background(), &userpb.GetUserRequest{Id: 1})21 if err != nil {22 log.Fatal(err)23 }24 log.Printf("User: %v", user)2526 // Server streaming27 stream, _ := client.ListUsers(context.Background(), &userpb.ListUsersRequest{})28 for {29 user, err := stream.Recv()30 if err != nil {31 break32 }33 log.Printf("User: %v", user)34 }35}
Интерцепторы
▸Логирование
1func loggingInterceptor(2 ctx context.Context,3 req interface{},4 info *grpc.UnaryServerInfo,5 handler grpc.UnaryHandler,6) (interface{}, error) {7 log.Printf("gRPC call: %s", info.FullMethod)8 start := time.Now()910 resp, err := handler(ctx, req)1112 log.Printf("gRPC call %s completed in %v", info.FullMethod, time.Since(start))13 return resp, err14}1516func main() {17 grpcServer := grpc.NewServer(18 grpc.UnaryInterceptor(loggingInterceptor),19 )20}
▸Аутентификация
1func authInterceptor(2 ctx context.Context,3 req interface{},4 info *grpc.UnaryServerInfo,5 handler grpc.UnaryHandler,6) (interface{}, error) {7 // Извлечение токена из metadata8 md, ok := metadata.FromIncomingContext(ctx)9 if !ok {10 return nil, status.Error(codes.Unauthenticated, "missing metadata")11 }1213 tokens := md.Get("authorization")14 if len(tokens) == 0 {15 return nil, status.Error(codes.Unauthenticated, "missing token")16 }1718 // Проверка токена19 userID, err := validateToken(tokens[0])20 if err != nil {21 return nil, status.Error(codes.Unauthenticated, "invalid token")22 }2324 // Добавление userID в контекст25 ctx = context.WithValue(ctx, "userID", userID)26 return handler(ctx, req)27}
Обработка ошибок
1import (2 "google.golang.org/grpc/codes"3 "google.golang.org/grpc/status"4)56func (s *server) GetUser(ctx context.Context, req *userpb.GetUserRequest) (*userpb.User, error) {7 user := db.Find(req.Id)8 if user == nil {9 return nil, status.Errorf(codes.NotFound, "user %d not found", req.Id)10 }11 return user, nil12}
Когда использовать gRPC
▸Используйте gRPC когда:
▸Используйте REST когда:
Заключение
gRPC — отличный выбор для микросервисов в Go. Protobuf обеспечивает типизацию и производительность, streaming позволяет работать с большими объёмами данных. На собеседовании спрашивают про разницу gRPC vs REST, когда использовать streaming и как обрабатывать ошибки.