带有 Hystrix 断路器超时的 Spring Cloud Feign 客户端默认为 2 秒

Posted

技术标签:

【中文标题】带有 Hystrix 断路器超时的 Spring Cloud Feign 客户端默认为 2 秒【英文标题】:Spring Cloud Feign client with Hystrix circuit-breaker timeout defaults in 2 seconds 【发布时间】:2020-11-27 15:43:42 【问题描述】:

可通过 GitHub 上的项目重现:spring-cloud-feign-hystrix-timeout-problem


我正在使用 Spring Boot 2.3.1.RELEASE 和 Spring Cloud Hoxton.SR6。即没有 Zuul 和 Eureka 的 Feign 客户端和 Hystrix 来获取 REST 响应。

<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-dependencies</artifactId>
   <version>Hoxton.SR6</version>
   <type>pom</type>
   <scope>import</scope>
</dependency>

然后我在 Spring Boot 2.3.1.RELEASESpring Cloud Hoxton.SR6 之上使用以下依赖项:

org.springframework.boot:spring-boot-starter org.springframework.boot:spring-boot-starter-web org.springframework.cloud:spring-cloud-starter-openfeign org.springframework.cloud:spring-cloud-starter-netflix-hystrix

我启用 @EnableFeignClients@EnableCircuitBreaker 并使用带有简单回退的 @FeignClient 来记录并重新抛出异常:

@FeignClient(name="my-feign", url = "$feign.url", fallbackFactory = MyFallbackFactory.class) 
public interface MyFeignClient 

    @PostMapping(value = "/api/dto")
    postDto(@RequestBody Dto dto);

使用以下application.yml,超时时间约为 1 秒,因为 Hystrix 默认使用相同的值:

feign:
  hystrix:
    enabled: true
  client:
    config:
      default:
        connectTimeout: 5000
        readTimeout: 5000

11:52:05.493 INFO 10200 --- [nio-8060-exec-2] com.mycompany.rest.MyController:现在调用 REST!

11:52:06.538 ERROR 24044 --- [nio-8060-exec-1] oaccC[.[.[/].[dispatcherServlet] :Servlet.service() 用于 servlet [dispatcherServlet] 在上下文中的路径 [ ] 抛出异常 [请求处理失败;嵌套异常是 com.netflix.hystrix.exception.HystrixRuntimeException: MyFeignClient#postDto(Dto) timed-out and fallback failed.] 根本原因


我尝试了什么?

只要我添加以下几行将超时时间增加到 60 秒,超时时间就会有效地变为 2 秒

hystrix:
  command:
    default:
      execution:
        timeout:
          enabled: true
        isolation:
          thread:
            timeoutInMilliseconds: 60000

11:53:33.590 INFO 16052 --- [nio-8060-exec-2] com.mycompany.rest.MyController:现在调用 REST!

11:53:35.614 ERROR 16052 --- [nio-8060-exec-2] oaccC[.[.[/].[dispatcherServlet] : Servlet.service() 用于 servlet [dispatcherServlet] 在上下文中的路径 [ ] 抛出异常 [请求处理失败;嵌套异常是 com.netflix.hystrix.exception.HystrixRuntimeException: MyFeignClient#postDto(Dto) failed and fallback failed.] 根本原因

只要增加了 Hystrix 读取/连接超时,调用就会在 2 秒 内立即回退。但是,只要我在 feign.client.config.default... 超时中声明它,我希望达到 5 秒。我觉得我缺少另一个配置。

问:如何增加超时时间?


编辑:

mvn dependency:tree: https://pastebin.com/LJFGaMTc pom.xml: https://pastebin.com/98uXHTaR 堆栈跟踪:https://pastebin.com/7rQweC8w

【问题讨论】:

我不知道这是否有帮助,请您最后检查一下这个文件中的 feign config gitlab.com/spring-boot-cloud-samples/… @silentsudo:谢谢! feign.client.config.default... 适用于所有服务,我没有进一步的配置。我也不使用 Resilience4j,而是使用 Hystrix。我的问题是否遵循最小且可重复的示例? 任何反对的理由?缺少什么? 【参考方案1】:

您的配置是正确的,您所描述的是预期行为。这里的问题是 Connection refused 的异常不会在您配置的超时后抛出 - 10 秒。相反,它会在 Java 的内部套接字实现发现服务器主机不可访问后立即抛出。在最简单的情况下,您调用的服务器没有启动和运行。

至于为什么设置了hystrix超时后还有第二次增加,可以调试hystrix的调用栈,发现HystrixRuntimeException的抛出顺序不一样。

在您的自定义 hystrix 超时之前,hystrix 的默认超时为 1 秒,这意味着此运行时异常总是在执行结束后一秒抛出一次,无论请求成功还是失败。所以在你的情况下,Connection refused 很可能发生在HystrixTimeoutException 之后。在您将超时设置为比 feign 客户端更长的时间后,HystrixTimeoutException 只有在 抛出 feign 异常(由于“连接被拒绝”)后才会创建,因此会延迟。

// 'cause' should be different due to timing
public HystrixRuntimeException(... Exception cause, Throwable fallbackException)

为了模拟超时,我想说你可以在服务器上强制超时,例如Thread.sleep(6000) 来停止服务器端的执行,或者只是在调试器上做一个断点。

【讨论】:

非常感谢!当Thread.sleep(6000) 提供服务时,我可以重现 5 秒超时。但是,配置名称会让我认为 Thread.sleep(6000) 导致 read timeout 而 not-available-service 导致 connection timeout - 两者都定义为 5 秒。跨度> 是的。属性名称并不能完全传达网络级别发生的情况。有细微的差别。假设客户端尝试连接到服务器。它首先尝试进行查找并向主机发送一个数据包,并且由于您的服务器甚至没有运行,它会立即发回一个导致“连接被拒绝”的数据包。另一方面,连接超时发生在客户端发送一个数据包但没有收到任何回报。 -- 继续上面 -- 这通常发生在与正在运行的服务器的初始连接期间,服务器需要超过分配的客户端超时时间才能响应。在您的情况下,这将很难重现,因为 Thread.sleep(6000) 导致读取超时,而不是连接超时,因为已建立初始连接。 @Nikolas 您获得了一致的 2 秒,因为在抛出 HystrixTimeoutException 之前的所有中间执行都没有改变。 2 秒的很大一部分是由于进行查找所花费的时间,并收到导致“连接被拒绝”的数据包。如果您从 localhost:8090 切换到涉及 DNS 查找的损坏的远程主机,这 2 秒可能会发生相当大的变化。 continue -- 请记住,在第一种情况下,由于 hystrix 默认超时,您会在一秒钟后得到一致的运行时错误,即使 Connection refused 的实际错误发生在一秒钟后。 HystrixTimeoutException 只是简单地包装首先出现的任何运行时异常并抛出它自己的一个实例。【参考方案2】:

您可以尝试的一件事是在一行中定义 Hystrix 超时的配置属性(我知道它应该等同于您在 YAML 文件中提出的嵌套定义,但有时我遇到的属性只有以这种方式加载)。正如您可以在他们的测试中see 一样,这是 Spring Cloud 本身配置 Hystrix 超时的方式:

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 5000

此外,尽管您表明您没有使用 Zuul 或 Eureka,但请验证您是否以任何方式使用 Ribbon,Hystrix 和 Ribbon 超时之间存在一些依赖关系(请参阅https://github.com/Netflix/Hystrix/issues/1778 和 https://github.com/spring-cloud/spring-cloud-netflix/issues/1324)。

如果是这种情况,则必须配置两个库的超时时间:

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 5000
ribbon:
  ConnectTimeout: 5000
  ReadTimeout: 5000

【讨论】:

感谢您的回答。首先,我尝试了配置的内联和 YAML 格式。我已将 ribbon.ConnectTimeoutribbon.ReadTimeout 设置为 5000,但是请求仅在 2 秒内而不是 5 秒内落入 Hystrix 回退。注意我不导入 spring-cloud-starter-netflix-ribbon,但是,spring-cloud-netflix-ribbon 作为传递依赖项存在。最后,我尝试了明确地导入此依赖项和/或排除当前的依赖项。 我看到@Nikolas。你能发布你的 Hystrix 命令定义吗? 没错,这个类就是我的意思,只是为了尝试重现错误。拜托,您能否扩展由 Hystrix 提升的运行时异常的堆栈跟踪的根本原因?另一方面,您只使用配置属性,不是吗?让我困扰的是为什么将 Hyxtrix 的超时时间定义为 60 秒会导致实际超时时间每 2 秒发生一次。为什么是 2 秒而不是其他时间?你是如何模拟故障转移的? 除了包含堆栈跟踪之外,我还将在 GitHub 上发布一个项目来重现此问题并保持问题清洁(稍后我下班回家后)。我使用标准的application.yml,没有别的(除了后备之外没有bean,它在 2 秒超时后调用)。我也想知道为什么有两秒钟。保持联系。 不客气。另一个答案似乎是正确的。这就是为什么我说如果你能显示完整的异常堆栈跟踪。普通套接字引发的超时对于理解问题非常有帮助。

以上是关于带有 Hystrix 断路器超时的 Spring Cloud Feign 客户端默认为 2 秒的主要内容,如果未能解决你的问题,请参考以下文章

Spring Cloud Hystrix 线程池隔离参数配置

spring-cloud-hystrix服务熔断与降级

Spring Cloud Hystrix介绍与使用

带有 feign 和 hystrix 的 Spring Boot:无法让请求超时工作

springCloud Spring Boot mybatis分布式微服务云架构-docker-hystrix-dashboard-turbine

springCloud Spring Boot mybatis分布式微服务云架构-docker-hystrix-dashboard-turbine