kratos icon indicating copy to clipboard operation
kratos copied to clipboard

[Question] 在grpc.Dial中设置的timeout有歧义

Open Nomango opened this issue 2 years ago • 3 comments

反馈一个作为用户有点迷惑的地方,我在联调一个耗时较长(约30s)的接口时,发现在 ctx 中设置的超时不生效,永远是2秒报超时,查看源码发现是 kratos 封装的 grpc 默认带了 2s 超时参数,通过 UnaryIntercepter 的方式 ”帮助“ 用户设置超时。

我觉得 grpc.WithTimeout 给 grpc.Dial 带来了一些歧义,一是在 Dial 设置的 timeout 却在 Perform 时生效, 二是 Dial 和 Perform 都有 ctx 入参可以控制超时,grpc.WithTimeout 却通过一个隐蔽的方式设置了一个”最小超时“,所以很容易写出一个超时bug,也会给用户造成困扰

Nomango avatar Aug 12 '22 02:08 Nomango

那个 grpc.WithTimeout 官方已经废弃了,DialContext 进行指定连接超时,kratos timeout 主要是保护客户端请求超时,或者服务端超时。比如发起了几个请求,然后总耗时为5s,其中有一个请求进行降级都是用户通过timeout控制。

tonybase avatar Aug 13 '22 05:08 tonybase

@tonybase 啊没写清楚,我写的grpc包是 kratos/v2/transport/grpc,举个例子

import "github.com/go-kratos/kratos/v2/transport/grpc"

// 在ctx中设置5秒超时
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()

// 在Dial Options中设置10秒超时,给人的感觉是timeout对dial本身生效,实际以ctx.timeout为准
conn, err := grpc.Dial(ctx, grpc.WithEndpoint("xxx"), grpc.WithTimeout(time.Second*10))

cli := service.NewXXClient(conn)

// 在调用接口的ctx中设置30秒超时
reqCtx, cancel := context.WithTimeout(context.Background(), time.Second*30)
defer cancel()

// PerformRequest 给人的感觉是以reqCtx.timeout为准,实际会取 DialOption.timeout 和 reqCtx.timeout 的最小值
cli.PerformRequest(reqCtx, req)

而在 grpc 官方包中的 timeout option 定义就比较明确:

WithTimeout returns a DialOption that configures a timeout for dialing a ClientConn initially.

也就是 Dial(WithTimeout()) 和 DialContext(ctxWithTimeout) 作用一样

然后我在使用 kratos.grpc 时,发现问题的过程就是这样的:

  1. 设置30秒请求超时没生效,排查ctx从上到下是不是误设置了 timeout
  2. 怀疑是不是下游返回的err就是timeout,也就是超时发生在server端的自己的代码里,只是抛给client之后由client打印了日志
  3. 无意间看到了grpc.Dial有一个timeout option,但是并没有觉得这和执行请求的超时有关
  4. 查看grpc.Dial源码,发现第一行就是timeout=2s,和实际2s报错一样
  5. 发现grpc.Dial(grpc.WithTimeout())会设置请求超时,而不是dial超时

歧义的点就是第5点,感觉需要在文档中强调一下 kratos.grpc 默认 2s 超时,如果需要在ctx中自定义超时,需要 grpc.WithTimeout(0) 来屏蔽默认超时

Nomango avatar Aug 15 '22 02:08 Nomango

我在调试一个接口超时问题时也遇到了类似的迷惑的点,我只能在所有可以设置超时的地方逐个测试超时时间对接口的影响。

ningzio avatar Jan 19 '23 10:01 ningzio

Hi, @Nomango! I'm Dosu, and I'm here to help the kratos team manage their backlog. I wanted to let you know that we are marking this issue as stale.

From what I understand, you were confused about the timeout behavior in grpc.Dial and grpc.WithTimeout. You found that the timeout set in the context does not work as expected and discovered that Kratos wraps the default gRPC timeout with 2 seconds. Tonybase explained that the timeout behavior in Kratos is different from the official gRPC package and provided an example. Ningzio also shared their experience with similar confusion and suggested conducting more tests to understand the impact of setting timeouts on individual APIs.

Before we close this issue, we wanted to check if it is still relevant to the latest version of the kratos repository. If it is, please let us know by commenting on the issue. Otherwise, feel free to close the issue yourself or it will be automatically closed in 7 days.

Thank you for your contribution to the kratos repository!

dosubot[bot] avatar Nov 03 '23 16:11 dosubot[bot]