我应该如何在GRPC调用中实现超时



我正在使用此代码连接到grpc服务器,clientConn对象用于所有后续的rpc调用。CCD_ 2被设置为5秒。现在,由于服务器上的一些问题,它没有响应grpc调用。因此,我的客户端为每个rpc调用等待很长时间。我需要用不同的方式设置超时吗?

b := grpc.BackoffConfig{
MaxDelay: maxDelay,
}
clientConn, err := grpc.Dial(serverAddress, grpc.WithBackoffConfig(b), grpc.WithInsecure())
if err != nil {
log.Println("Dial failed!")
return err
}

您可以使用grpc.WithTimeout(5 * time.Second)修改代码以添加超时,而不是使用用于重试和重试延迟的MaxDelaygrpc.WithBackoffConfig(b)

clientConn, err := grpc.Dial(serverAddress, grpc.WithTimeout(5 * time.Second), grpc.WithInsecure())
if err != nil {
log.Println("Dial failed!")
return err
}

然而,上述内容已被弃用,您也可以使用DialContextcontext.WithTimeout

ctx, _ := context.WithTimeout(context.Background(), 5*time.Second)
clientConn, err := grpc.DialContext(ctx, serverAddress, grpc.WithInsecure())
if err != nil {
log.Println("Dial failed!")
return err
}

context.WithTimeoutgrpc.DialContext中用于控制当前DialContext的所有RPC调用的超时。处理一个/一些特定RPC调用的不同超时是不方便的。


我们可以定义一个自定义超时callOption来处理clientInterceptor中某些RPC调用的强制超时值

定义自定义超时callOptionEmptyCallOption

type TimeoutCallOption struct {
grpc.EmptyCallOption
forcedTimeout time.Duration
}
func WithForcedTimeout(forceTimeout time.Duration) TimeoutCallOption {
return TimeoutCallOption{forcedTimeout: forceTimeout}
}

UnaryClientInterceptor中处理forcedTimeout

func getForcedTimeout(callOptions []grpc.CallOption) (time.Duration, bool) {
for _, opt := range callOptions {
if co, ok := opt.(TimeoutCallOption); ok {
return co.forcedTimeout, true
}
}
return 0, false
}
func TimeoutInterceptor(t time.Duration) grpc.UnaryClientInterceptor {
return func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn,
invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
timeout := t
if v, ok := getForcedTimeout(opts); ok {
timeout = v
}
if timeout <= 0 {
return invoker(ctx, method, req, reply, cc, opts...)
}
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
return invoker(ctx, method, req, reply, cc, opts...)
}
}

使用示例

// The default timeout of RPC call of this conn is 3 seconds
conn, err := grpc.Dial(
address,
grpc.WithUnaryInterceptor(TimeoutInterceptor(time.Duration(3)*time.Second)), ...)
....
c := pb.NewGreeterClient(conn)
c.SayHello(context.Background(), &pb.HelloRequest{Name: "world"},
WithForcedTimeout(time.Duration(10)*time.Second))
// The timeout of SayHello RPC call is 10 seconds

WithTimeout的doc说它用于设置连接初始化的超时,而不是为调用设置超时。DialContext中的上下文是相同的。

WithTimeout返回一个DialOption,用于配置拨号超时最初是ClientConn。当且仅当WithBlock((为目前不推荐:使用DialContext而不是Dial和上下文改为WithTimeout。将支持整个1.x.

要设置呼叫超时,可以将context传递给invoke,如:

ctx, cancel := context.WithDeadline(ctx, time.Now().Add(time.Duration(2000)*time.Millisecond))
defer cancel()
clientConn.Invoke(ctx, "/YourEndpoint", in, out, opts...)

最新更新