Feign 超时时间明明设了 3 秒,为什么实际等了 10 秒才返回超时?

发布时间:2026/8/29 4:35:38
Feign 超时时间明明设了 3 秒,为什么实际等了 10 秒才返回超时? 这个问题我相信不少人遇到过。配置文件里写得清清楚楚feign: client: config: default: connectTimeout: 3000 readTimeout: 3000下游服务一旦变慢或者挂掉按预期最多 3 秒就应该超时返回。但实际一看监控超时时间稳定在 10 秒左右。3 秒变 10 秒多出来的 7 秒是哪来的先搞清楚一件事你配的 Feign 超时可能根本没生效很多人不知道在Spring Cloud 2020.0之前的版本对应 Spring Boot 2.4 之前Feign 底层的 HTTP 调用不是自己发出去的而是委托给 Ribbon 的 LoadBalancerFeignClient 来执行的。你可以在项目里查一下mvn dependency:tree | grep ribbon如果看到spring-cloud-starter-netflix-ribbon那就对了Ribbon是实际干活的那个。而 Ribbon 有自己的超时配置默认值写在 DefaultClientConfigImpl 里public static final int DEFAULT_CONNECT_TIMEOUT 2000; public static final int DEFAULT_READ_TIMEOUT 5000;ReadTimeout 默认 5000 毫秒。你在 Feign 层配的 3000ms到 Ribbon 这层被它自己的默认值 5000ms 盖掉了。但 5 秒也不是 10 秒还差一半问题出在哪另一半时间Ribbon 的默认重试Ribbon 有两个和重试相关的参数// 同一台实例重试次数不含首次 ribbon.MaxAutoRetries 0 // 切换下一台实例的重试次数 ribbon.MaxAutoRetriesNextServer 1 MaxAutoRetriesNextServer 1 意味着第一台实例超时之后Ribbon 会自动切换到另一台实例再试一次。实际最大等待时间的计算公式总耗时 ReadTimeout × (MaxAutoRetries 1) × (MaxAutoRetriesNextServer 1)代入默认值5000 × (0 1) × (1 1) 5000 × 1 × 2 10000ms10 秒对上了。第一台实例等 5 秒超时切到第二台再等 5 秒超时一共 10 秒。整个过程中你配的那个 3 秒没有参与任何环节。为什么 Ribbon 能覆盖 Feign 的配置不是猜的看源码。FeignLoadBalancer 在执行请求时构造超时参数的逻辑Override public RibbonResponse execute(RibbonRequest request, IClientConfig configOverride) throws IOException { Request.Options options; if (configOverride ! null) { RibbonProperties override RibbonProperties.from(configOverride); options new Request.Options( override.connectTimeout(this.connectTimeout), override.readTimeout(this.readTimeout) ); } else { options new Request.Options(this.connectTimeout, this.readTimeout); } // ... }this.connectTimeout和this.readTimeout来自 Ribbon 的 IClientConfig不是来自 Feign 的配置。Feign 层面设的超时值在这里被直接无视了。配置优先级实际上是这样的Ribbon 服务级别配置 Ribbon 全局配置 Ribbon 默认值 Feign 配置不生效怎么改知道原因了改法很直接。第一种配 Ribbon 的参数既然实际生效的是 Ribbon就直接配 Ribbonribbon: ConnectTimeout: 3000 ReadTimeout: 3000 MaxAutoRetries: 0 MaxAutoRetriesNextServer: 0MaxAutoRetriesNextServer建议改成 0。默认的重试行为对 GET 请求问题不大但如果是 POST 请求没有幂等保证的情况下重试一次可能直接造成业务重复——比如多扣一笔款、多发一条短信。需要对特定服务单独设置的话order-service: ribbon: ReadTimeout: 5000 MaxAutoRetries: 0 MaxAutoRetriesNextServer: 0第二种升级 Spring Cloud去掉 RibbonSpring Cloud 2020.0 开始Ribbon 被移除了替代方案是Spring Cloud LoadBalancer。升级之后 Feign 的超时配置才是真正生效的那一份feign: client: config: default: connectTimeout: 3000 readTimeout: 3000如果项目短期内升不上去就老实配 Ribbon不要在 Feign 层面配了之后以为万事大吉。第三种在上层用熔断器兜底如果你对底层超时配置的优先级已经搞不清了可以在 Feign 外面再套一层Sentinel或者Resilience4j做超时控制。不管底层实际等多久上层到了时间直接走fallback。多一层封装但确定性强。怎么验证配置到底生效没有写一个测试接口故意 sleep 超过超时时间GetMapping(/slow) public String slow() throws InterruptedException { Thread.sleep(15000); return done; }用 Feign 调这个接口看实际多久返回异常约 3 秒返回你的超时配置生效了约 5 秒返回Ribbon 默认超时在生效重试关了约 10 秒返回Ribbon 默认超时 默认重试你配的 Feign 超时完全没用不用猜跑一次就知道了。说在最后这个问题的根源就一句话在有 Ribbon 的 Spring Cloud 项目里Feign 的超时配置是不生效的实际控制超时的是 Ribbon。配超时的时候先搞清楚最终是谁在发 HTTP 请求然后去配那一层的参数。中间封装了几层就有几层你以为生效了其实没生效的可能。