3、超时机制概述:超时与重试、熔断的关系

聊超时机制之前,我想先问大家一个问题:你遇到过服务调用卡死的情况吗?

我在项目中遇到过好几次。有一次线上告警,某个接口的响应时间从 50ms 突然飙到 30 秒,整个线程池被打满,后续请求全部排队。最后发现是下游服务挂了,但我们的客户端没设超时,一直在傻等。

嗯,这就是典型的没有超时保护的后果。

超时机制,说白了就是给每个请求设定一个“等待上限”。超过这个时间,不管结果如何,先放弃再说。但超时不是孤立存在的,它和重试、熔断是铁三角关系。今天我们就来拆解这三者是怎么配合的。

3.1 超时的本质:给不确定性设一个边界

分布式系统里,网络延迟、服务负载、GC 停顿……这些不确定性太多了。你没法保证每次调用都能在 100ms 内返回。但你可以决定:我等多久就不再等了

我个人习惯把超时分为两类:

  • 连接超时:建立 TCP 连接的最长等待时间。一般设 500ms~1s。
  • 读取超时:连接建立后,等待响应数据的最长时间。这个要根据业务来,我通常设 2s~5s。

核心原则:超时时间 = 正常 P99 延迟 × 1.5 ~ 2 倍。别拍脑袋设个 30 秒,那等于没设。

3.2 超时与重试:一对相爱相杀的兄弟

超时之后怎么办?很多人第一反应就是重试。但重试不是万能的,用不好反而会雪上加霜。

我曾经犯过一个错误:某个核心服务设了 3 次重试,每次超时 2 秒。结果下游服务 GC 抖动,所有请求都超时,然后重试又发起新请求,直接把下游打挂了。这就是典型的重试风暴

所以,超时和重试必须搭配使用,规则如下:

场景 是否重试 说明
连接超时 建议重试 可能是网络抖动,重试成功率较高
读取超时 谨慎重试 可能是服务端处理慢,重试可能加重负载
业务异常(如参数错误) 不重试 重试一万次也是同样的结果
幂等性不保证 不重试 重试可能导致数据重复

我的经验:重试一定要加退避策略。比如第一次失败后等 100ms,第二次等 200ms,第三次等 400ms。别一股脑全扎进去。

3.3 超时与熔断:从“等待”到“主动拒绝”

重试解决不了所有问题。如果下游服务已经挂了,你重试再多次也没用。这时候就需要熔断机制出场了。

熔断的核心思想很简单:连续失败达到阈值,直接短路,不再发起请求

你想想看,如果某个接口连续超时 10 次,说明什么?说明下游大概率已经不可用了。这时候再发请求,除了浪费资源,没有任何意义。

超时和熔断的关系是这样的:

  • 超时是熔断的输入信号:每次超时都算一次失败,累计到阈值触发熔断。
  • 熔断是超时的保护机制:熔断开启后,请求直接返回失败,不再等待超时。
  • 半开状态:熔断一段时间后,放少量请求试探,如果成功则关闭熔断,如果超时则继续熔断。

注意:熔断的阈值不能设得太低。我曾经设了 3 次失败就熔断,结果一次 GC 抖动就熔断了半小时,影响面很大。建议设 10~20 次,配合滑动窗口统计。

3.4 三者的协作流程

为了让你更直观地理解,我画了一张流程图:

发起请求 是否超时? (等待时间 > 阈值) 返回成功结果 熔断器状态? (关闭/半开/开启) 关闭/半开 是否重试? (重试次数 < 阈值) 是(退避等待) 开启 快速失败(熔断) 返回超时异常

这张图展示了完整的流程:

  1. 发起请求后,先等待响应。
  2. 如果没超时,正常返回结果。
  3. 如果超时了,检查熔断器状态。
  4. 熔断器关闭或半开,尝试重试(带退避)。
  5. 熔断器开启,直接快速失败,不重试。
  6. 重试次数用完,返回超时异常,并上报给熔断器。

3.5 实际项目中的配置建议

说了这么多理论,最后给点干货。这是我目前在用的配置模板:

# 超时配置
rpc:
  timeout:
    connect: 500ms      # 连接超时
    read: 3000ms        # 读取超时
    total: 10000ms      # 整体超时(含重试)

# 重试配置
retry:
  maxAttempts: 3        # 最大重试次数
  backoff: 
    type: exponential   # 指数退避
    initialDelay: 100ms # 初始延迟
    multiplier: 2       # 倍数
    maxDelay: 2000ms    # 最大延迟

# 熔断配置
circuitBreaker:
  failureThreshold: 10          # 失败阈值
  successThreshold: 5           # 成功阈值(半开状态)
  windowSize: 60s               # 滑动窗口大小
  openDuration: 30s             # 熔断持续时间

避坑指南:我曾经把重试退避的倍数设成 5,结果第一次等 100ms,第二次等 500ms,第三次等 2500ms……总耗时远超预期。建议倍数控制在 1.5~2 之间。

总结一下:超时是底线,重试是补偿,熔断是保护。三者缺一不可,但也不能滥用。设好参数,做好监控,才能让系统在故障面前稳如泰山。


公众号:蓝海资料掘金营,微信deep3321