英文:
Spring Cloud Feign client with Hystrix circuit-breaker timeout defaults in 2 seconds
问题
以下是您要翻译的内容:
通过GitHub上的项目可重现:spring-cloud-feign-hystrix-timeout-problem
我正在使用Spring Boot 2.3.1.RELEASE
与Spring Cloud Hoxton.SR6
。具体来说,使用Feign客户端和Hystrix,而不使用Zuul和Eureka来获取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.RELEASE
和Spring 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 : Calling REST right now!
>
> 11:52:06.538 ERROR 24044 --- [nio-8060-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is com.netflix.hystrix.exception.HystrixRuntimeException: MyFeignClient#postDto(Dto) timed-out and fallback failed.] with root cause
我尝试了什么?
只要我添加以下行以增加超时时间到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 : Calling REST right now!
>
> 11:53:35.614 ERROR 16052 --- [nio-8060-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is com.netflix.hystrix.exception.HystrixRuntimeException: MyFeignClient#postDto(Dto) failed and fallback failed.] with root cause
只要增加了Hystrix的读取/连接超时时间,调用就会在2秒内陷入回退状态。但是,根据我在feign.client.config.default...
中声明的超时时间,我希望实现5秒。我感觉我错过了另一个配置。
问:如何增加超时时间?
编辑:
mvn dependency:tree
: https://pastebin.com/LJFGaMTcpom.xml
: https://pastebin.com/98uXHTaR- 堆栈跟踪: https://pastebin.com/7rQweC8w
英文:
Reproducible through a project on GitHub: spring-cloud-feign-hystrix-timeout-problem
I am using Spring Boot 2.3.1.RELEASE
with Spring Cloud Hoxton.SR6
. Namely Feign client and Hystrix without Zuul and Eureka to fetch REST responses.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR6</version>
<type>pom</type>
<scope>import</scope>
</dependency>
Then I use use the following dependencies on top of Spring Boot 2.3.1.RELEASE
and Spring 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
I enable @EnableFeignClients
and @EnableCircuitBreaker
and use a @FeignClient
with a simple fallback to log and rethrow an exception:
@FeignClient(name="my-feign", url = "${feign.url}", fallbackFactory = MyFallbackFactory.class) {
public interface MyFeignClient {
@PostMapping(value = "/api/dto")
postDto(@RequestBody Dto dto);
}
With the following application.yml
the timeout is around 1 second because Hystrix defaults to the very same value:
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 : Calling REST right now!
>
> 11:52:06.538 ERROR 24044 --- [nio-8060-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is com.netflix.hystrix.exception.HystrixRuntimeException: MyFeignClient#postDto(Dto) timed-out and fallback failed.] with root cause
What I tried?
As long as I add the following lines to increase the timeouts to 60 seconds, the timeout become effectively around 2 seconds:
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 : Calling REST right now!
>
> 11:53:35.614 ERROR 16052 --- [nio-8060-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is com.netflix.hystrix.exception.HystrixRuntimeException: MyFeignClient#postDto(Dto) failed and fallback failed.] with root cause
The call falls into fallback right in 2 seconds as long as the Hystrix read/connect timeouts have been increased. However I expect to achieve 5 seconds as long as I declared it in feign.client.config.default...
timeouts. I feel I am missing another configuration.
Q: How to increase the timeout?
Edit:
mvn dependency:tree
: https://pastebin.com/LJFGaMTcpom.xml
: https://pastebin.com/98uXHTaR- Stack-trace: https://pastebin.com/7rQweC8w
答案1
得分: 4
你的配置是正确的,你描述的行为是预期的。问题在于,当出现Connection refused
异常时,并没有在你配置的超时时间——10秒后抛出。相反,在Java的内部套接字实现发现服务器主机不可达后立即抛出该异常。在最简单的情况下,你所调用的服务器可能没有启动。
至于为什么在你设置了Hystrix超时后会有第二次增加,你可以调试Hystrix的调用堆栈,找出HystrixRuntimeException
的抛出顺序不同。
在你自定义的Hystrix超时之前,Hystrix的默认超时时间为1秒,这意味着一旦执行时间超过1秒,无论请求成功还是失败,都会抛出这个运行时异常。所以在你的情况下,Connection refused
很可能会在HystrixTimeoutException
之后发生。当你将超时时间设置得比Feign客户端的超时时间长时,HystrixTimeoutException
只会在Feign异常(由于"Connection refused")抛出之后才被创建,因此会有延迟。
为了模拟超时,你可以在服务器端强制超时,比如使用Thread.sleep(6000)
来延迟服务器端的执行,或者在调试器中设置断点。
英文:
Your configuration is correct and what you are describing is expected behavior. The issue here is that exception with Connection refused
isn't thrown after your configured timeout -- 10 seconds. Instead, it is thrown immediately after Java's internal socket implementation finds that server host is not reachable. In simplest case, the server you are calling isn't up and running.
As to why there is a second increase after you set the hystrix timeout, you can debug hystrix's call stack to find out that HystrixRuntimeException
isn't being thrown in the same order.
Before your custom hystrix timeout, hystrix had a default timeout of 1 second, which means this runtime exception is always thrown once one second since execution has lapsed, regardless whether the request succeeds or fails. So in your case, Connection refused
could very likely happen after HystrixTimeoutException
. After you set the timeout be to longer than that of feign client, HystrixTimeoutException
only gets created after a feign exception is thrown (due to "Connection refused"), hence the delay.
// 'cause' should be different due to timing
public HystrixRuntimeException(... Exception cause, Throwable fallbackException)
To simulate the timeout, I'd say you could force a timeout on the server, such as Thread.sleep(6000)
to halt the execution on server side, or simply do a breakpoint while on debugger.
答案2
得分: 2
你可以尝试的一件事是将Hystrix超时的配置属性定义在一行中(我知道这应该等同于你在YAML文件中提出的嵌套定义,但有时我遇到只能以这种方式加载的属性)。正如你可以在他们的测试中看到的,这是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
英文:
One thing you can try is to define the configuration property of the Hystrix timeout in a single line (I know that it should be equivalent to the nested definition you proposed in your YAML file, but sometimes I came across properties that only were loaded in this way). As you can see in their tests, this is the way Spring Cloud itself configures Hystrix timeout:
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 5000
Also, although you indicated that you are not using Zuul or Eureka, please, verify if you are using Ribbon in any way, there is some dependencies (see https://github.com/Netflix/Hystrix/issues/1778 and https://github.com/spring-cloud/spring-cloud-netflix/issues/1324) between the Hystrix and Ribbon timeouts.
If it is the case, you must configure the timeouts of both libraries:
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 5000
ribbon:
ConnectTimeout: 5000
ReadTimeout: 5000
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论