Açık kaynaklı bir uzaktan prosedür çağrısı (RPC) çerçevesi olan gRPC, hizmetler arasında verimli ve ölçeklenebilir iletişim sağlar. GRPC'nin önemli yönlerinden biri son tarihlerin, istek zaman aşımlarının ve özel yapılar da dahil olmak üzere bağlamın yayılmasının yönetimidir.
Bu mekanizmaları anlamak, hizmetlerin anında yanıt vermesini, kaynakların makul bir zaman dilimini aşan işlemlerde israf edilmemesini ve özel meta verilerin etkili bir şekilde iletilmesini sağlamaya yardımcı olur.
GRPC'deki son tarih, bir işlemin tamamlanması gereken maksimum süreyi belirtir. İşlemin bu süre içerisinde tamamlanmaması durumunda otomatik olarak sonlandırılacaktır. Yanıt vermeyen veya yavaş hizmetler nedeniyle sistem kaynaklarının süresiz olarak bağlı kalmamasını sağlamak için son tarihler önemlidir.
İstek zaman aşımı, istemcinin sunucudan yanıt beklemek istediği süredir. Sunucu bu süre içerisinde yanıt vermezse istek iptal edilir. Bu mekanizma, istemcinin süresiz olarak yanıt bekleyerek askıda kalmasını önler.
gRPC, hem istemci hem de sunucu tarafında son tarihlerin belirlenmesi ve zaman aşımlarının talep edilmesi için esnek seçenekler sunar. Go'da bunu nasıl yapabileceğiniz aşağıda açıklanmıştır:
import ( "context" "log" "time" "google.golang.org/grpc" pb "path/to/your/protobuf/package" ) func main() { conn, err := grpc.Dial("server_address", grpc.WithInsecure()) if err != nil { log.Fatalf("did not connect: %v", err) } defer conn.Close() client := pb.NewYourServiceClient(conn) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() resp, err := client.YourMethod(ctx, &pb.YourRequest{}) if err != nil { log.Fatalf("could not call method: %v", err) } log.Printf("Response: %v", resp) }
Sunucu tarafında gRPC, son tarihleri uygulamanıza ve istemci tarafından belirtilen son tarihin aşıldığı senaryoları yönetmenize olanak tanır:
import ( "context" "log" "net" "time" "google.golang.org/grpc" pb "path/to/your/protobuf/package" ) type server struct { pb.UnimplementedYourServiceServer } func (s *server) YourMethod(ctx context.Context, req *pb.YourRequest) (*pb.YourResponse, error) { select { case <-time.After(10 * time.Second): return &pb.YourResponse{}, nil case <-ctx.Done(): return nil, ctx.Err() } } func main() { lis, err := net.Listen("tcp", ":50051") if err != nil { log.Fatalf("failed to listen: %v", err) } s := grpc.NewServer() pb.RegisterYourServiceServer(s, &server{}) if err := s.Serve(lis); err != nil { log.Fatalf("failed to serve: %v", err) } }
Özel yapıları gRPC'de bağlam yoluyla göndermek için, verileri bağlama eklemeden önce serileştirmeniz ve ardından alıcı tarafta seri durumdan çıkarmanız gerekir. Bu, özel yapılarınızı JSON veya Protokol Arabellekleri gibi ağ üzerinden aktarılabilecek bir formata dönüştürmeyi ve ardından bu serileştirilmiş verileri bağlam meta verilerine eklemeyi içerir.
type CustomStruct struct { Field1 string Field2 int }
Adım 2: Yapıyı Serileştirin
import ( "context" "encoding/json" "fmt" "google.golang.org/grpc/metadata" ) func serializeCustomStruct(customStruct CustomStruct) (string, error) { data, err := json.Marshal(customStruct) if err != nil { return "", err } return string(data), nil }
3. Adım: Bağlama Ekle
func attachCustomStructToContext(ctx context.Context, customStruct CustomStruct) (context.Context, error) { serializedData, err := serializeCustomStruct(customStruct) if err != nil { return nil, err } md := metadata.Pairs("custom-struct", serializedData) ctx = metadata.NewOutgoingContext(ctx, md) return ctx, nil }
Adım 4: İletim
func main() { conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure()) if err != nil { log.Fatalf("did not connect: %v", err) } defer conn.Close() client := pb.NewYourServiceClient(conn) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() customStruct := CustomStruct{Field1: "value1", Field2: 42} ctx, err = attachCustomStructToContext(ctx, customStruct) if err != nil { log.Fatalf("could not attach custom struct to context: %v", err) } resp, err := client.YourMethod(ctx, &pb.YourRequest{}) if err != nil { log.Fatalf("could not call method: %v", err) } log.Printf("Response: %v", resp) }
Adım 5: Sunucuda Çıkarma ve Seri Durumdan Çıkarma
func deserializeCustomStruct(data string) (CustomStruct, error) { var customStruct CustomStruct err := json.Unmarshal([]byte(data), &customStruct) if err != nil { return CustomStruct{}, err } return customStruct, nil } func extractCustomStructFromContext(ctx context.Context) (CustomStruct, error) { md, ok := metadata.FromIncomingContext(ctx) if !ok { return CustomStruct{}, fmt.Errorf("no metadata found in context") } serializedData := md["custom-struct"] if len(serializedData) == 0 { return CustomStruct{}, fmt.Errorf("no custom struct found in metadata") } return deserializeCustomStruct(serializedData[0]) } func (s *server) YourMethod(ctx context.Context, req *pb.YourRequest) (*pb.YourResponse, error) { customStruct, err := extractCustomStructFromContext(ctx) if err != nil { return nil, err } log.Printf("Received custom struct: %+v", customStruct) select { case <-time.After(10 * time.Second): return &pb.YourResponse{}, nil case <-ctx.Done(): return nil, ctx.Err() } }
Tüm gRPC çağrılarında özel yapılar da dahil olmak üzere bağlam yayılımını tutarlı bir şekilde yönetmek için önleyicileri kullanabilirsiniz. Durdurucular, istekleri ve yanıtları işleyen, günlüğe kaydetme, izleme ve bağlam meta veri işleme gibi işlevler ekleyen ara yazılımlardır.
Farklı türdeki RPC çağrılarını yönetmek için hem tekli hem de akış önleyicilere ihtiyacınız vardır:
İstemci Tarafı Tekli Durdurucu:
func unaryClientInterceptor( ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption, ) error { customStruct, ok := ctx.Value("customStruct").(CustomStruct) if ok { ctx, err := attachCustomStructToContext(ctx, customStruct) if err != nil { return err } } return invoker(ctx, method, req, reply, cc, opts...) }
Sunucu Tarafı Tekli Durdurucu:
func unaryServerInterceptor( ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler, ) (interface{}, error) { customStruct, err := extractCustomStructFromContext(ctx) if err != nil { return nil, err } ctx = context.WithValue(ctx, "customStruct", customStruct) return handler(ctx, req) }
İstemci Tarafı Akış Durdurucusu:
func streamClientInterceptor( ctx context.Context, desc *grpc.StreamDesc, cc *grpc.ClientConn, method string, streamer grpc.Streamer, opts ...grpc.CallOption, ) (grpc.ClientStream, error) { customStruct, ok := ctx.Value("customStruct").(CustomStruct) if ok { ctx, err := attachCustomStructToContext(ctx, customStruct) if err != nil { return nil, err } } return
Sunucu Tarafı Akış Durdurucusu:
import ( "context" "google.golang.org/grpc" "google.golang.org/grpc/metadata" ) // StreamServerInterceptor handles server-side streaming func streamServerInterceptor( srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler, ) error { ctx := ss.Context() customStruct, err := extractCustomStructFromContext(ctx) if err != nil { return err } // Add custom struct to context for server handling newCtx := context.WithValue(ctx, "customStruct", customStruct) wrapped := grpc_middleware.WrapServerStream(ss) wrapped.WrappedContext = newCtx // Handle the request return handler(srv, wrapped) } // Example of using the interceptor in a gRPC server setup func main() { lis, err := net.Listen("tcp", ":50051") if err != nil { log.Fatalf("failed to listen: %v", err) } // Register the interceptors server := grpc.NewServer( grpc.UnaryInterceptor(unaryServerInterceptor), grpc.StreamInterceptor(streamServerInterceptor), ) // Register your gRPC service implementations here pb.RegisterYourServiceServer(server, &yourServiceServer{}) if err := server.Serve(lis); err != nil { log.Fatalf("failed to serve: %v", err) } }
Tekli ve akış yakalayıcılar oluşturup kaydederek, özel yapılar da dahil olmak üzere bağlam yayılımının tüm gRPC çağrılarında tutarlı bir şekilde işlenmesini sağlayabilirsiniz. Bu yaklaşım, özel meta verilerinizin uygun şekilde yönetilmesini ve yayılmasını sağlayarak sağlam ve esnek gRPC hizmetleri oluşturmanıza olanak tanır.