微服务中远程调用的超时时间应该设置为多少合适?

Sherwin.Wei Lv7

微服务中远程调用的超时时间应该设置为多少合适?

回答重点

一般远程调用的默认超时时间为 2-5s。但在实际工作中,还是要看具体的业务场景,因为不同的业务场景对超时时间的要求不同。

  • 一些查询、验证等较为简单的操作,超时时间可以适当缩短。
  • 一些需要长时间处理的业务,比如大数据处理、复杂的查询,可能需要配置较长的超时时间,避免超时过早导致的失败。

除此之外,超时时间应该基于服务的响应能力进行设置,也就是考虑服务端(服务提供者)的处理时间,如果服务端有历史响应时间数据或监控信息,可以参考这些数据进行合理设置,例如参考 TP99 的耗时来设置超时时间

整体的配置根据当前的上下游服务情况敲定一个合理的超时时间。

一般来说,建议服务端(服务提供者)提供配置超时时间和重试次数等参数,因为作为服务提供方,它比消费方更清楚当前服务、方法的性能。而消费者直接采纳生产者提供的配置即可

扩展知识

远程调用超时时间

在微服务场景,远程调用超时时间,一般指的是生产者(provider)和消费者(consumer)配置的方法调用超时时间。

超时时间无法设置过短。如果时间过短,正常业务流程还未走完,消费者检测到接口超时,直接中断调用,影响正常业务的执行。

超时时间无法设置过长。如果时间过长,此时请求量较大,那么就会出现大量长期无响应的请求占用线程池等情况,导致资源被占用无法释放,严重会导致服务崩溃。

因此超时时间的设置需要比正常业务耗时长,但是又不能长太多,建议根据压测情况。参考 TP99 的耗时来设置超时时间。

TP99 指的是满足 99% 次数的网络请求所需要的最低耗时。比如调用一个方法,1 小时内调用了 10w 次,监控显示 TP99 是 300ms,这个意思就是:这个请求强度下,百分之 99% 的调用,都可以在 300ms 内返回结果。此时超时时间可以设置为 1s,给一定的冗余又不会太多,此时设置 10s 就是过长的配置。

RPC 框架的多粒度超时时间

例如 dubbo 支持多粒度配置 rpc 调用的超时时间。

参考官网的例子,我们来看下多种粒度:

1)配置全局默认超时时间为 5s(不配置的情况下,所有服务的默认超时时间是 1s

1
2
3
dubbo:
provider:
timeout: 5000

2)在消费端,指定 DemoService 服务调用的超时时间为 5s

1
2
@DubboReference(timeout=5000)
private DemoService demoService;

3)在提供端,指定 DemoService 服务调用的超时时间为 5s(可作为所有消费端的默认值,如果消费端有指定则优先级更高)

1
2
@DubboService(timeout=5000)
public class DemoServiceImpl implements DemoService{}

4)在消费端,指定 DemoService sayHello 方法调用的超时时间为 5s

1
2
@DubboReference(methods = {@Method(name = "sayHello", timeout = 5000)})
private DemoService demoService;

5)在提供端,指定 DemoService sayHello 方法调用的超时时间为 5s(可作为所有消费端的默认值,如果消费端有指定则优先级更高)

1
2
@DubboService(methods = {@Method(name = "sayHello", timeout = 5000)})
public class DemoServiceImpl implements DemoService{}

以上配置形式的优先级从高到低依次为:方法级别配置 > 服务级别配置 > 全局配置 > 默认值。

Dubbo Deadline 机制

Deadline 其实就是一个在长链路调用中,发现调用超时触发的一个提前终止操作。我们来看下 dubbo 官网的例子:

image.png

我们来分析一下以上调用链路以及可能出现的超时情况:

  • A 调用 B 设置了超时时间 5s,因此 B -> C -> D 总计耗时不应该超过 5s,否则 A 就会收到超时异常
  • 在任何情形下,只要 A 等待 5s 没有收到响应,整个调用链路就可以被终止了(如果此时 C 正在运行,则 C -> D 就没有发起的意义了)
  • 理论上 B -> C、C -> D 都有自己独立的超时时间设置,超时计时也是独立计算的,它们不知道 A 作为调用发起方是否超时

在 Dubbo 框架中,A -> B 的调用就像一个开关,一旦启动,在任何情形下整个 A -> B -> C -> D 调用链路都会被完整执行下去,即便调用方 A 已经超时,后续的调用动作仍会继续。

这在一些场景下是没有意义的,尤其是链路较长的情况下会带来不必要的资源消耗,deadline 就是设计用来解决这个问题,通过在调用链路中传递 deadline(deadline初始值等于超时时间,随着时间流逝而减少)可以确保调用链路只在有效期内执行,deadline 消耗殆尽之后,调用链路中其他尚未执行的任务将被取消。

因此 deadline 机制就是将 B -> C -> D 当作一个整体看待,这一系列动作必须在 5s 之内完成。随着时间流逝 deadline 会从 5s 逐步扣减,后续每一次调用实际可用的超时时间即是当前 deadline 值,比如 C 收到请求时已经过去了 3s,则 C -> D 的超时时间只剩下 2s。

image.png

deadline 机制默认是关闭的,如果要启用 deadline 机制,需要配置以下参数:

1
2
3
4
dubbo:
provider:
timeout: 5000
parameters.enable-timeout-countdown: true

也可以指定某个服务调用开启 deadline 机制:

1
2
@DubboReference(timeout=5000, parameters={"enable-timeout-countdown", "true"})
private DemoService demoService;

动态调整服务超时时间

动态调整服务超时时间主要应用在突发状况下,无需重启应用临时调整服务的超时时间,解决上下游依赖不稳定而导致的调用失败的问题。

部分 rpc 框架支持动态调整服务超时时间,例如 dubbo 可以通过 Dubbo Admin 动态修改超时时间。

操作步骤

  1. 打开 Dubbo Admin 控制台
  2. 在左侧导航栏选择【服务治理】>【动态配置】
  3. 点击 “创建”,输入服务 org.apache.dubbo.samples.UserService 和新的超时时间如 2000 即可。

image.png

底层原理

  • 动态配置监听:Dubbo在启动时注册了配置中心的监听器,以监控相关配置项的变化。当timeout配置发生变更时,Dubbo会收到通知。
  • 反射更新:收到更新通知后,Dubbo会使用反射机制更新相关的超时配置。通过动态代理的方式,新的超时时间会被应用到相应的服务调用中。
  • 超时控制:在每次调用服务时,Dubbo会根据当前的超时时间来判断请求是否需要被中断。如果请求未在设定的超时时间内完成,Dubbo会主动抛出超时异常,确保系统的稳定性。
Comments