paint-brush
gRPC-Secret: Nắm vững thời hạn, thời gian chờ và bối cảnh tùy chỉnhtừ tác giả@gultm
888 lượt đọc
888 lượt đọc

gRPC-Secret: Nắm vững thời hạn, thời gian chờ và bối cảnh tùy chỉnh

từ tác giả Tatyana9m2024/08/01
Read on Terminal Reader

dài quá đọc không nổi

gRPC là một khung gọi thủ tục từ xa (RPC) nguồn mở. Nó cho phép giao tiếp hiệu quả và có thể mở rộng giữa các dịch vụ. Một khía cạnh quan trọng là quản lý thời hạn, thời gian chờ yêu cầu và truyền bá ngữ cảnh. Hiểu các cơ chế này giúp đảm bảo rằng các dịch vụ phản hồi kịp thời.
featured image - gRPC-Secret: Nắm vững thời hạn, thời gian chờ và bối cảnh tùy chỉnh
Tatyana HackerNoon profile picture

gRPC, khung gọi thủ tục từ xa (RPC) nguồn mở, cho phép giao tiếp hiệu quả và có thể mở rộng giữa các dịch vụ. Một khía cạnh quan trọng của gRPC là quản lý thời hạn, thời gian chờ yêu cầu và truyền bá ngữ cảnh, bao gồm cả các cấu trúc tùy chỉnh.


Việc hiểu các cơ chế này giúp đảm bảo rằng các dịch vụ phản hồi kịp thời, tài nguyên không bị lãng phí cho các hoạt động vượt quá khung thời gian hợp lý và siêu dữ liệu tùy chỉnh được truyền đi một cách hiệu quả.

Hiểu thời hạn và thời gian chờ yêu cầu

thời hạn

Thời hạn trong gRPC chỉ định thời gian tối đa mà một thao tác phải được hoàn thành. Nếu hoạt động không được hoàn thành trong khung thời gian này, nó sẽ tự động bị chấm dứt. Thời hạn là điều cần thiết để đảm bảo rằng tài nguyên hệ thống không bị ràng buộc vô thời hạn do dịch vụ không phản hồi hoặc chậm.

Yêu cầu hết thời gian

Thời gian chờ yêu cầu là khoảng thời gian mà khách hàng sẵn sàng chờ phản hồi từ máy chủ. Nếu máy chủ không phản hồi trong khoảng thời gian này, yêu cầu sẽ bị hủy bỏ. Cơ chế này bảo vệ máy khách không bị treo vô thời hạn chờ phản hồi.

Đặt thời hạn và thời gian chờ yêu cầu trong gRPC

gRPC cung cấp các tùy chọn linh hoạt để đặt thời hạn và yêu cầu thời gian chờ ở cả phía máy khách và máy chủ. Đây là cách bạn có thể làm điều đó trong Go:

Thời hạn thiết lập phía khách hàng


 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) }

Xử lý phía máy chủ

Về phía máy chủ, gRPC cho phép bạn thực thi thời hạn và xử lý các tình huống vượt quá thời hạn do khách hàng chỉ định:


 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) } }


Tuyên truyền cấu trúc tùy chỉnh trong ngữ cảnh

Để gửi cấu trúc tùy chỉnh qua ngữ cảnh trong gRPC, bạn cần tuần tự hóa dữ liệu trước khi đính kèm nó vào ngữ cảnh và sau đó giải tuần tự hóa dữ liệu đó ở đầu nhận. Điều này liên quan đến việc chuyển đổi cấu trúc tùy chỉnh của bạn thành định dạng có thể được truyền qua mạng, chẳng hạn như JSON hoặc Bộ đệm giao thức, sau đó thêm dữ liệu được tuần tự hóa này vào siêu dữ liệu ngữ cảnh.

Quy trình từng bước

  1. Xác định cấu trúc tùy chỉnh của bạn : Xác định cấu trúc tùy chỉnh bạn muốn gửi.
  2. Serialize the Structure : Chuyển đổi cấu trúc tùy chỉnh thành một chuỗi hoặc mảng byte.
  3. Đính kèm vào ngữ cảnh : Thêm dữ liệu được tuần tự hóa vào siêu dữ liệu ngữ cảnh.
  4. Truyền : Gửi lệnh gọi gRPC kèm theo ngữ cảnh.
  5. Trích xuất và giải tuần tự hóa trên máy chủ : Trích xuất siêu dữ liệu từ ngữ cảnh ở phía máy chủ và giải tuần tự hóa nó trở lại cấu trúc tùy chỉnh.

Bước 1: Xác định cấu trúc tùy chỉnh của bạn


 type CustomStruct struct { Field1 string Field2 int }


Bước 2: Tuần tự hóa cấu trúc


 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 }


Bước 3: Đính kèm vào bối cảnh


 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 }


Bước 4: Truyền


 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) }


Bước 5: Giải nén và giải tuần tự hóa trên máy chủ


 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() } }


Triển khai Middleware cho tất cả lệnh gọi gRPC

Để xử lý việc truyền bá ngữ cảnh, bao gồm các cấu trúc tùy chỉnh, một cách nhất quán trên tất cả các lệnh gọi gRPC, bạn có thể sử dụng bộ chặn. Bộ chặn là phần mềm trung gian xử lý các yêu cầu và phản hồi, bổ sung các chức năng như ghi nhật ký, giám sát và xử lý siêu dữ liệu ngữ cảnh.

Thiết bị chặn đơn nhất và phát trực tuyến

Bạn cần cả thiết bị chặn đơn tuyến và thiết bị chặn phát trực tuyến để quản lý các loại cuộc gọi RPC khác nhau:


  • Bộ chặn đơn nhất : Xử lý các chu kỳ phản hồi yêu cầu duy nhất.


  • Bộ chặn luồng : Xử lý các luồng yêu cầu và phản hồi, bao gồm phát trực tuyến phía máy khách, phát trực tuyến phía máy chủ và phát trực tuyến hai chiều.

Thực hiện đánh chặn đơn nhất

Bộ chặn đơn nhất phía máy khách:


 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...) }


Bộ chặn đơn nhất phía máy chủ:


 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) }

Triển khai chặn phát trực tuyến

Bộ chặn phát trực tuyến phía máy khách:


 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


Bộ chặn phát trực tuyến phía máy chủ:


 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) } }


Bằng cách tạo và đăng ký các bộ chặn đơn tuyến và phát trực tuyến, bạn có thể đảm bảo rằng việc truyền ngữ cảnh, bao gồm cả các cấu trúc tùy chỉnh, được xử lý nhất quán trên tất cả các lệnh gọi gRPC. Cách tiếp cận này đảm bảo rằng siêu dữ liệu tùy chỉnh của bạn được quản lý và phổ biến đúng cách, cho phép bạn xây dựng các dịch vụ gRPC mạnh mẽ và linh hoạt.