本文共 5369 字,大约阅读时间需要 17 分钟。
在使用gRPC时,客户端和服务器之间的连接、序列化、反序列化以及超时执行是常见的操作。Deadlines(截止时间)允许客户端设置等待多长时间完成RPC操作的限制,直到出现错误DEADLINE_EXCEEDED。默认情况下,DEADLINE_EXCEEDED的值通常设置得很高。
如果没有设置Deadlines,所有请求可能会在最大请求时间过后才超时,这会导致服务器资源消耗增加,如内存膨胀等问题。因此,为客户端请求程序设置一个默认超时时间是必要的,当请求在此时间内未返回时,会超时报错。
var deadlineMs = flag.Int("deadline_ms", 20*1000, "Default deadline in milliseconds.")clientDeadline := time.Now().Add(time.Duration(*deadlineMs) * time.Millisecond)ctx, cancel := context.WithDeadline(ctx, clientDeadline) if ctx.Err() == context.Canceled { return status.New(codes.Canceled, "Client cancelled, abandoning.")} Dial()函数位于google.golang.org/grpc/clientconn.go中,实际执行由DialContext()函数完成。客户端应传入设置超时的context,如下所示:
ctx, cancel := context.Timeout(context.Background(), time.Second*5)defer cancel()conn, err := grpc.DialContext(ctx, address, grpc.WithBlock(), grpc.WithInsecure())
grpc.WithInsecure():跳过对服务器证书的验证,适用于练习,但在生产环境中不建议使用。grpc.WithBlock():阻塞等待握手成功,否则超时控制失效。ctx, cancel := context.WithTimeout(context.TODO(), time.Second*5)defer cancel()result, err := c.SayHello(ctx, &pb.HelloRequest{Name: name}) echo.protosyntax = "proto3";package echo;message EchoRequest { string message = 1;}message EchoResponse { string message = 1;}service Echo { rpc UnaryEcho(EchoRequest) returns (EchoRequest) {} rpc ServerStreamingEcho(EchoRequest) returns (stream EchoResponse) {} rpc ClientStreamingEcho(stream EchoRequest) returns (EchoResponse) {} rpc BidirectionalStreamingEcho(stream EchoRequest) returns (stream EchoResponse) {}} client/main.go// unaryCall 不是stream的请求func unaryCall(c pb.EchoClient, requestID int, message string, want codes.Code) { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() req := &pb.EchoRequest{Message: message} _, err := c.UnaryEcho(ctx, req) got := status.Code(err) fmt.Printf "[%v] wanted = %v, got = %v\n", requestID, want, got} // streamingCall,2端都是streamfunc streamingCall(c pb.EchoClient, requestID int, message string, want codes.Code) { ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() stream, err := c.BidirectionalStreamingEcho(ctx) if err != nil { log.Printf("Send error : %v", err) return } err = stream.Send(&pb.EchoRequest{Message: message}) if err != nil { log.Printf("Send error : %v", err) return } _, err = stream.Recv() got := status.Code(err) fmt.Printf "[%v] wanted = %v, got = %v\n", requestID, want, got} func main() { flag.Parse() conn, err := grpc.Dial(*addr, grpc.WithInsecure(), grpc.WithBlock()) if err != nil { log.Fatalf("did not connect : %v ", err) } defer conn.Close() c := pb.NewEchoClient(conn) // 成功请求 unaryCall(c, 1, "word", codes.OK) // 超时 deadline unaryCall(c, 2, "delay", codes.DeadlineExceeded) // A successful request with propagated deadline unaryCall(c, 3, "[propagate me]world", codes.OK) // Exceeds propagated deadline unaryCall(c, 4, "[propagate me][propagate me]world", codes.DeadlineExceeded) // Receives a response from the stream successfully. streamingCall(c, 5, "[propagate me]world", codes.OK) // Exceeds propagated deadline before receiving a response streamingCall(c, 6, "[propagate me][propagate me]world", codes.DeadlineExceeded)} server/main.gotype server struct { pb.UnimplementedEchoServer client pb.EchoClient cc *grpc.ClientConn} func (s *server) UnaryEcho(ctx context.Context, req *pb.EchoRequest) (*pb.EchoResponse, error) { message := req.Message if strings.HasPrefix(message, "[propagate me]") { time.Sleep(800 * time.Millisecond) message := strings.TrimPrefix(message, "[propagate me]") return s.client.UnaryEcho(ctx, &pb.EchoRequest{Message: message}) } if message == "delay" { time.Sleep(1500 * time.Millisecond) } return &pb.EchoResponse{Message: message}, nil} func (s *server) BidirectionalStreamingEcho(stream pb.Echo_BidirectionalStreamingEchoServer) error { for { req, err := stream.Recv() if err == io.EOF { return status.Error(codes.InvalidArgument, "request message not received") } if err != nil { return err } message := req.Message if strings.HasPrefix(message, "[propagate me]") { time.Sleep(800 * time.Millisecond) message := strings.TrimPrefix(message, "[propagate me]") res, err := s.client.UnaryEcho(stream.Context(), &pb.EchoRequest{Message: message}) if err != nil { return err } stream.Send(res) } if message == "delay" { time.Sleep(1500 * time.Millisecond) } stream.Send(&pb.EchoResponse{Message: message}) }} 运行server/main.go和client/main.go,执行结果如下:
go run main.go[1] wanted = OK, got = OK[2] wanted = DeadlineExceeded, got = DeadlineExceeded[3] wanted = OK, got = Unavailable[4] wanted = DeadlineExceeded, got = Unavailable[5] wanted = OK, got = Unavailable[6] wanted = DeadlineExceeded, got = Unavailable
转载地址:http://hzfyz.baihongyu.com/