如何在GRPC调用中实现超时?

问题描述 投票:0回答:4

我使用此代码连接到 grpc 服务器,并且

clientConn
对象用于所有后续的 rpc 调用。
maxDelay
设置为 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
}
go grpc
4个回答
8
投票

您可以修改代码以使用

grpc.WithTimeout(5 * time.Second)
添加超时,而不是使用用于重试和重试延迟的
MaxDelay
grpc.WithBackoffConfig(b)

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

但是上面的内容已被弃用,您也可以使用

DialContext
context.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
}

2
投票

context.WithTimeout
中使用
grpc.DialContext
来控制当前DialContext的所有RPC调用的超时时间。处理一个/某些特定 RPC 调用的不同超时很不方便。


我们可以定义一个自定义超时

callOption
来处理
clientInterceptor

中某些RPC调用的强制超时值

通过

callOption
 定义自定义超时 
EmptyCallOption

type TimeoutCallOption struct {
    grpc.EmptyCallOption

    forcedTimeout time.Duration
}

func WithForcedTimeout(forceTimeout time.Duration) TimeoutCallOption {
    return TimeoutCallOption{forcedTimeout: forceTimeout}
}

处理

forcedTimeout
中的
UnaryClientInterceptor

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


2
投票

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

WithTimeout 返回一个 DialOption,配置拨号超时时间 最初是 ClientConn。当且仅当 WithBlock() 为 展示。已弃用:使用 DialContext 代替 Dial 和 context.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...)

0
投票

使用

grpc.WithTimeout
的旧语法已被弃用。

opts := []grpc.DialOption{
  // you would also need grpc.WithBlock() which can be an anti-pattern
  // grpc.WithBlock(),
  grpc.WithTimeout(timeoutValue),
  // ...
}
clientConn, err := grpc.Dial(serverAddress, opts...)
if err != nil {
    return err
}

https://pkg.go.dev/google.golang.org/grpc#WithTimeout

WithTimeout
返回一个
DialOption
,配置最初拨打
ClientConn
的超时。当且仅当
WithBlock()
存在时,这才有效。

已弃用:使用

DialContext
代替
Dial
context.WithTimeout
代替。将在整个 1.x.

中得到支持

因此,如果您使用新语法,这仍然仅适用于拨打初始

ClientConn
的超时。

opts := []grpc.DialOption{
  ...
}
ctx, cancel := context.WithTimeout(context.Background(), timeoutValue)
clientConn, err := grpc.DialContext(ctx, serverAddress, opts...)
if err != nil {
    return err
}

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

如果您希望每次通话都有超时;那么这就超出了最初的

ClientConn
。您可能希望提供使用
context.WithTimeout
并将其传递给
ClientConn.Invoke
。所以你可以自己做,或者配置一个超时拦截器。

https://pkg.go.dev/google.golang.org/grpc#ClientConn.Invoke

有 2 个不错的包可用于配置常见的客户端拦截器以实现超时和重试:

import (
    "context"
    "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/timeout"
)

opts := []grpc.DialOption{
    // timeout for each unary call
    timeout.UnaryClientInterceptor(timeoutValue),
    // ...
}

// timeout for initial ClientConn
ctx, cancel := context.WithTimeout(context.Background(), timeoutValue)
clientConn, err := grpc.DialContext(ctx, serverAddress, opts...)
if err != nil {
    return err
}
© www.soinside.com 2019 - 2024. All rights reserved.