目录
- 1.什么是Feign超时
- 2.Ribbon超时
- 3.Hystrix超时
- 4.在Feign中禁用Hystrix
- 5.同时设置Ribbon超时和Hystrix超时
- 7.Ribbon超时值和Hystrix超时值如何设定?
- 7.1.Feign超时结论
- 7.2.ribbon的重试机制
1.什么是Feign超时
1.使用
Feign
调用接口分为两层,
Ribbon
的调用和
Hystrix
的调用,所以
Ribbon
的超时时间和
Hystrix
的超时时间的结合就是
Feign
的超时时间。
2.一般情况下 都是
Ribbon
的超时时间(<)
Hystrix
的超时时间(因为涉及到
Ribbon
的重试机制),如果
Ribbon
的超时时间大于Hystrix的超时时间,对于
Ribbon
的重试是没有意义的(
Hystrix
超时熔断了,
Ribbon
无法重试)。
提示:
- 默认情况下,如果Ribbon没有配置重试时间和次数,默认是1S超时,默认会自动重试1次。
- 默认情况下,如果Hystrix没有配置重试时间和次数,默认是1S超时,默认会自动重试1次。
2.Ribbon超时
问题: 在服务提供者的接口上加休眠5秒,测试Feign调用的时候是否会超时?
@GetMapping(value = "/getProductInfo/{productId}")
public String getProductInfo(@PathVariable("productId") String productId) {
log.info("请求进来啦");
try {
log.info("模拟服务端超时");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
}
return "Hello Nacos Discovery " + productId;
}
接口提供方和接口调用方的
application.yml
都是如下:
server:
port: 8716
spring:
cloud:
nacos:
discovery:
#服务注册与发现地址
server-addr: 127.0.0.1:8848
#开启服务注册与发现功能
enabled: true
application:
name: nacos-product
访问接口:http://localhost:8777/getProduct/100 控制台异常如下:

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is feign.RetryableException: Read timed out executing GET http://nacos-product/getProductInfo/100
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) ~[spring-webmvc-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:503) ~[jakarta.servlet-api-4.0.3.jar:4.0.3]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at javax.servlet.http.HttpServlet.service(HttpServlet.java:590) ~[jakarta.servlet-api-4.0.3.jar:4.0.3]
at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74) ~[undertow-servlet-2.0.29.Final.jar:2.0.29.Final]
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:129) ~[undertow-servlet-2.0.29.Final.jar:2.0.29.Final]
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61) ~[undertow-servlet-2.0.29.Final.jar:2.0.29.Final]
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131) ~[undertow-servlet-2.0.29.Final.jar:2.0.29.Final]
at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61) ~[undertow-servlet-2.0.29.Final.jar:2.0.29.Final]
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131) ~[undertow-servlet-2.0.29.Final.jar:2.0.29.Final]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at io.undertow.servlet.core.ManagedFilter.doFilter(ManagedFilter.java:61) ~[undertow-servlet-2.0.29.Final.jar:2.0.29.Final]
at io.undertow.servlet.handlers.FilterHandler$FilterChainImpl.doFilter(FilterHandler.java:131) ~[undertow-servlet-2.0.29.Final.jar:2.0.29.Final]
at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:84) ~[undertow-servlet-2.0.29.Final.jar:2.0.29.Final]
at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62) ~[undertow-servlet-2.0.29.Final.jar:2.0.29.Final]
at io.undertow.servlet.handlers.ServletChain$1.handleRequest(ServletChain.java:68) ~[undertow-servlet-2.0.29.Final.jar:2.0.29.Final]
at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36) ~[undertow-servlet-2.0.29.Final.jar:2.0.29.Final]
at io.undertow.servlet.handlers.RedirectDirHandler.handleRequest(RedirectDirHandler.java:68) ~[undertow-servlet-2.0.29.Final.jar:2.0.29.Final]
at io.undertow.servlet.handlers.security.SSLInformationAssociationHandler.handleRequest(SSLInformationAssociationHandler.java:132) ~[undertow-servlet-2.0.29.Final.jar:2.0.29.Final]
at io.undertow.servlet.handlers.security.ServletAuthenticationCallHandler.handleRequest(ServletAuthenticationCallHandler.java:57) ~[undertow-servlet-2.0.29.Final.jar:2.0.29.Final]
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) ~[undertow-core-2.0.29.Final.jar:2.0.29.Final]
at io.undertow.security.handlers.AbstractConfidentialityHandler.handleRequest(AbstractConfidentialityHandler.java:46) ~[undertow-core-2.0.29.Final.jar:2.0.29.Final]
at io.undertow.servlet.handlers.security.ServletConfidentialityConstraintHandler.handleRequest(ServletConfidentialityConstraintHandler.java:64) ~[undertow-servlet-2.0.29.Final.jar:2.0.29.Final]
at io.undertow.security.handlers.AuthenticationMechanismsHandler.handleRequest(AuthenticationMechanismsHandler.java:60) ~[undertow-core-2.0.29.Final.jar:2.0.29.Final]
at io.undertow.servlet.handlers.security.CachedAuthenticatedSessionHandler.handleRequest(CachedAuthenticatedSessionHandler.java:77) ~[undertow-servlet-2.0.29.Final.jar:2.0.29.Final]
at io.undertow.security.handlers.AbstractSecurityContextAssociationHandler.handleRequest(AbstractSecurityContextAssociationHandler.java:43) ~[undertow-core-2.0.29.Final.jar:2.0.29.Final]
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) ~[undertow-core-2.0.29.Final.jar:2.0.29.Final]
at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43) ~[undertow-core-2.0.29.Final.jar:2.0.29.Final]
at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:269) [undertow-servlet-2.0.29.Final.jar:2.0.29.Final]
at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:78) [undertow-servlet-2.0.29.Final.jar:2.0.29.Final]
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:133) [undertow-servlet-2.0.29.Final.jar:2.0.29.Final]
at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:130) [undertow-servlet-2.0.29.Final.jar:2.0.29.Final]
at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48) [undertow-servlet-2.0.29.Final.jar:2.0.29.Final]
at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43) [undertow-servlet-2.0.29.Final.jar:2.0.29.Final]
at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:249) [undertow-servlet-2.0.29.Final.jar:2.0.29.Final]
at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:78) [undertow-servlet-2.0.29.Final.jar:2.0.29.Final]
at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:99) [undertow-servlet-2.0.29.Final.jar:2.0.29.Final]
at io.undertow.server.Connectors.executeRootHandler(Connectors.java:376) [undertow-core-2.0.29.Final.jar:2.0.29.Final]
at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:830) [undertow-core-2.0.29.Final.jar:2.0.29.Final]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_40]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_40]
at java.lang.Thread.run(Thread.java:745) [na:1.8.0_40]
Caused by: feign.RetryableException: Read timed out executing GET http://nacos-product/getProductInfo/100
at feign.FeignException.errorExecuting(FeignException.java:249) ~[feign-core-10.7.4.jar:na]
at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:120) ~[feign-core-10.7.4.jar:na]
at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:80) ~[feign-core-10.7.4.jar:na]
at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:100) ~[feign-core-10.7.4.jar:na]
at com.sun.proxy.$Proxy85.getProductInfo(Unknown Source) ~[na:na]
at com.bruce.controller.OrderController.getProduct(OrderController.java:25) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_40]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_40]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_40]
at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_40]
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:106) ~[spring-webmvc-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:879) ~[spring-webmvc-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793) ~[spring-webmvc-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) ~[spring-webmvc-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.2.4.RELEASE.jar:5.2.4.RELEASE]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.2.4.RELEASE.jar:5.2.4.RELEASE]
... 47 common frames omitted
Caused by: java.net.SocketTimeoutException: Read timed out
at java.net.SocketInputStream.socketRead0(Native Method) ~[na:1.8.0_40]
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116) ~[na:1.8.0_40]
at java.net.SocketInputStream.read(SocketInputStream.java:170) ~[na:1.8.0_40]
at java.net.SocketInputStream.read(SocketInputStream.java:141) ~[na:1.8.0_40]
at java.io.BufferedInputStream.fill(BufferedInputStream.java:246) ~[na:1.8.0_40]
at java.io.BufferedInputStream.read1(BufferedInputStream.java:286) ~[na:1.8.0_40]
at java.io.BufferedInputStream.read(BufferedInputStream.java:345) ~[na:1.8.0_40]
at sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:704) ~[na:1.8.0_40]
at sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:647) ~[na:1.8.0_40]
at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1535) ~[na:1.8.0_40]
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1440) ~[na:1.8.0_40]
at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480) ~[na:1.8.0_40]
at feign.Client$Default.convertResponse(Client.java:78) ~[feign-core-10.7.4.jar:na]
at feign.Client$Default.execute(Client.java:74) ~[feign-core-10.7.4.jar:na]
at org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer.execute(FeignLoadBalancer.java:93) ~[spring-cloud-openfeign-core-2.2.2.RELEASE.jar:2.2.2.RELEASE]
at org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer.execute(FeignLoadBalancer.java:56) ~[spring-cloud-openfeign-core-2.2.2.RELEASE.jar:2.2.2.RELEASE]
at com.netflix.client.AbstractLoadBalancerAwareClient$1.call(AbstractLoadBalancerAwareClient.java:104) ~[ribbon-loadbalancer-2.3.0.jar:2.3.0]
at com.netflix.loadbalancer.reactive.LoadBalancerCommand$3$1.call(LoadBalancerCommand.java:303) ~[ribbon-loadbalancer-2.3.0.jar:2.3.0]
at com.netflix.loadbalancer.reactive.LoadBalancerCommand$3$1.call(LoadBalancerCommand.java:287) ~[ribbon-loadbalancer-2.3.0.jar:2.3.0]
at rx.internal.util.ScalarSynchronousObservable$3.call(ScalarSynchronousObservable.java:231) ~[rxjava-1.3.8.jar:1.3.8]
at rx.internal.util.ScalarSynchronousObservable$3.call(ScalarSynchronousObservable.java:228) ~[rxjava-1.3.8.jar:1.3.8]
at rx.Observable.unsafeSubscribe(Observable.java:10327) ~[rxjava-1.3.8.jar:1.3.8]
at rx.internal.operators.OnSubscribeConcatMap$ConcatMapSubscriber.drain(OnSubscribeConcatMap.java:286) ~[rxjava-1.3.8.jar:1.3.8]
at rx.internal.operators.OnSubscribeConcatMap$ConcatMapSubscriber.onNext(OnSubscribeConcatMap.java:144) ~[rxjava-1.3.8.jar:1.3.8]
at com.netflix.loadbalancer.reactive.LoadBalancerCommand$1.call(LoadBalancerCommand.java:185) ~[ribbon-loadbalancer-2.3.0.jar:2.3.0]
at com.netflix.loadbalancer.reactive.LoadBalancerCommand$1.call(LoadBalancerCommand.java:180) ~[ribbon-loadbalancer-2.3.0.jar:2.3.0]
at rx.Observable.unsafeSubscribe(Observable.java:10327) ~[rxjava-1.3.8.jar:1.3.8]
at rx.internal.operators.OnSubscribeConcatMap.call(OnSubscribeConcatMap.java:94) ~[rxjava-1.3.8.jar:1.3.8]
at rx.internal.operators.OnSubscribeConcatMap.call(OnSubscribeConcatMap.java:42) ~[rxjava-1.3.8.jar:1.3.8]
at rx.Observable.unsafeSubscribe(Observable.java:10327) ~[rxjava-1.3.8.jar:1.3.8]
at rx.internal.operators.OperatorRetryWithPredicate$SourceSubscriber$1.call(OperatorRetryWithPredicate.java:127) ~[rxjava-1.3.8.jar:1.3.8]
at rx.internal.schedulers.TrampolineScheduler$InnerCurrentThreadScheduler.enqueue(TrampolineScheduler.java:73) ~[rxjava-1.3.8.jar:1.3.8]
at rx.internal.schedulers.TrampolineScheduler$InnerCurrentThreadScheduler.schedule(TrampolineScheduler.java:52) ~[rxjava-1.3.8.jar:1.3.8]
at rx.internal.operators.OperatorRetryWithPredicate$SourceSubscriber.onNext(OperatorRetryWithPredicate.java:79) ~[rxjava-1.3.8.jar:1.3.8]
at rx.internal.operators.OperatorRetryWithPredicate$SourceSubscriber.onNext(OperatorRetryWithPredicate.java:45) ~[rxjava-1.3.8.jar:1.3.8]
at rx.internal.util.ScalarSynchronousObservable$WeakSingleProducer.request(ScalarSynchronousObservable.java:276) ~[rxjava-1.3.8.jar:1.3.8]
at rx.Subscriber.setProducer(Subscriber.java:209) ~[rxjava-1.3.8.jar:1.3.8]
at rx.internal.util.ScalarSynchronousObservable$JustOnSubscribe.call(ScalarSynchronousObservable.java:138) ~[rxjava-1.3.8.jar:1.3.8]
at rx.internal.util.ScalarSynchronousObservable$JustOnSubscribe.call(ScalarSynchronousObservable.java:129) ~[rxjava-1.3.8.jar:1.3.8]
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) ~[rxjava-1.3.8.jar:1.3.8]
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) ~[rxjava-1.3.8.jar:1.3.8]
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) ~[rxjava-1.3.8.jar:1.3.8]
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) ~[rxjava-1.3.8.jar:1.3.8]
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48) ~[rxjava-1.3.8.jar:1.3.8]
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30) ~[rxjava-1.3.8.jar:1.3.8]
at rx.Observable.subscribe(Observable.java:10423) ~[rxjava-1.3.8.jar:1.3.8]
at rx.Observable.subscribe(Observable.java:10390) ~[rxjava-1.3.8.jar:1.3.8]
at rx.observables.BlockingObservable.blockForSingle(BlockingObservable.java:443) ~[rxjava-1.3.8.jar:1.3.8]
at rx.observables.BlockingObservable.single(BlockingObservable.java:340) ~[rxjava-1.3.8.jar:1.3.8]
at com.netflix.client.AbstractLoadBalancerAwareClient.executeWithLoadBalancer(AbstractLoadBalancerAwareClient.java:112) ~[ribbon-loadbalancer-2.3.0.jar:2.3.0]
at org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient.execute(LoadBalancerFeignClient.java:83) ~[spring-cloud-openfeign-core-2.2.2.RELEASE.jar:2.2.2.RELEASE]
at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:110) ~[feign-core-10.7.4.jar:na]
... 64 common frames omitted
分析: 出现这个异常的时候,起初我不知道究竟是
Hystrix
断路器走熔断了还是
Ribbon
处理请求超时了,通过观察日志和做测试,我发现了错误日志是由:
org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer
包打印出来的,那么我就很确定这是
Ribbon
处理请求超时了,因为我设置了接口提供方休眠
5
秒,而Ribbon的默认超时是
1
秒,而默认情况下在使用Feign时,
Hystrix默认是未开启的
,这样就更加肯定这是Ribbon超时,那么如何处理呢,见后文。
org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer.execute(FeignLoadBalancer.java:93) ~[spring-cloud-openfeign-core-2.1.2.RELEASE.jar:2.1.2.RELEASE]
解决: 服务调用方
application.yml
加入
Ribbon
请求处理超时和连接超时,这里需要注意一下,我们在服务提供方的接口里设置休眠的时间是5秒,所以这里在设置
Ribbon
的超时时间时,需要大于5秒,不然起不到作用。
server:
port: 8777
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
enabled: true
application:
name: nacos-order
# 调用端匹配值
ribbon:
ReadTimeout: 6000
ConnectTimeout: 5000
再次请求:http://localhost:8777/getProduct/100
正如我们初步的判断,上述异常在Feign消费服务,
Feign
中的
Hystrix
默认是未开启的,所以这个问题是由Ribbon超时引起的。
3.Hystrix超时
问题: 在服务提供者的接口上加休眠5秒,测试Feign调用的时候是否会超时?
@GetMapping(value = "/getProductInfo/{productId}")
public String getProductInfo(@PathVariable("productId") String productId) {
log.info("请求进来啦");
try {
log.info("模拟服务端超时");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
}
return "Hello Nacos Discovery " + productId;
}
接口提供方的
application.yml
配置改成如下,为了探究
Hystrix
我们去掉
Ribbon
的超时配置,开启
Feign
中的
Hystrix
:
访问接口:http://localhost:8777/getProduct/100 控制台异常如下:
2021-02-20 10:44:54.878 ERROR 8012 --- [ HystrixTimer-1] c.b.client.OrderFeignFallbackFactory : 调用异常:com.netflix.hystrix.exception.HystrixTimeoutException
2021-02-20 10:44:54.881 INFO 8012 --- [ XNIO-1 task-1] com.bruce.controller.OrderController : 调用服务结束: 开启断路-fallback; reason was: com.netflix.hystrix.exception.HystrixTimeoutException
2021-02-20 10:44:55.075 INFO 8012 --- [erListUpdater-0] c.netflix.config.ChainedDynamicProperty : Flipping property: nacos-product.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
控制台打印日志:
分析: 从异常的日志来看,服务调用超时了,超时后
Hystrix
立即进行了服务的熔断,走了回调方法
OrderFeignFallbackFactory
,输出了服务熔断后我们定制的Msg,那么如何解决这个超时问题呢?
package com.bruce.client;
import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* @BelongsProject: springcloud-alibaba-nacos
* @BelongsPackage: com.bruce.client
* @CreateTime: 2021-02-20 10:42
* @Description: TODO
*/
@Component
@Slf4j
public class OrderFeignFallbackFactory implements FallbackFactory<OrderFeign> {
@Override
public OrderFeign create(final Throwable throwable) {
log.error("调用异常:" + throwable.toString());
return new OrderFeign() {
@Override
public String getProductInfo(String string) {
return "开启断路-fallback; reason was: " + throwable.toString();
}
};
}
}
解决: 在接口调用方
application.yml
加入
Hystrix
超时配置,同样这里需要注意的是,
Hystrix
默认的超时时间是
1
秒,而我们在服务提供方的接口里设置休眠的时间是5秒,所以这里在设置Hystrix的超时时间时,需要大于
5
秒,不然起不到作用防止超时的作用。
server:
port: 8777
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
enabled: true
application:
name: nacos-order
feign:
hystrix:
enabled: true
hystrix:
command:
default:
execution:
timeout:
enabled: true
isolation:
thread:
timeoutInMilliseconds: 60000
#ribbon:
# ReadTimeout: 6000
# ConnectTimeout: 5000
再次访问接口:http://localhost:8777/getProduct/100 控制台异常如下:
控制台异常如下:
分析: 开启了Hystrix断路器并且设置了
Hystrix
的超时时间为6秒,但是这个时候Ribbon处理请求却超时了。分析下面这行异常日志:
RetryableException
大概意思是重试异常,查阅资料得知,Ribbon在超时后会进行重试的,因为没有对
Ribbon
超时进行处理,但是开启了
Hystirx
的断路器功能和设置其超时,所以
Ribbon
在处理请求超时的时候,Hystirx就出来起作用了,对服务进行熔断,降级处理,进而进入回调方法
OrderFeignFallbackFactory
,返回友好提示起到服务保护作用。
调用异常:feign.RetryableException: Read timed out executing GET http://nacos-product/getProductInfo/1010110
总结: 通过上述分析,那么在开启Hystrix断路器功能之后就需要对
Ribbon
进行超时设置了,两者在碰到Feign时,需要协同作战,或者更粗暴点直接不开启Hystrix断路器功能,但是这种方式极不推荐,高并发的项目中服务保护必不可少,所以我们需要将
Ribbon
与Hystrix结合使用,那么接下来我们需要着重讨论
Ribbon
与
Hystrix
如何协同作战。
4.在Feign中禁用Hystrix
禁用Hystrix:
参考官网资料: https://cloud.spring.io/spring-cloud-static/spring-cloud-openfeign/2.2.2.RELEASE/reference/html/#spring-cloud-feign-hystrix
要基于每个客户端禁用
Hystrix
支持,请创建
Feign.Builder
具有“原型”范围的香草,这段话摘自官网,例如:
package com.bruce.client;
import feign.Feign;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
@Configuration
public class FooConfiguration {
@Bean
@Scope("prototype")
public Feign.Builder feignBuilder() {
return Feign.builder();
}
}
5.同时设置Ribbon超时和Hystrix超时
Feign
调用时同时设置
Ribbon
超时和
Hystrix
超时,
application.yml
配置如下
server:
port: 8777
spring:
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
enabled: true
application:
name: nacos-order
feign:
hystrix:
enabled: true
hystrix:
command:
default:
execution:
timeout:
enabled: true
isolation:
thread:
timeoutInMilliseconds: 120000
ribbon:
ReadTimeout: 6000
ConnectTimeout: 5000
访问接口: http://localhost:8777/getProduct/100
7.Ribbon超时值和Hystrix超时值如何设定?
注意: Ribbon超时+Hystrix超时= Feign超时
7.1.Feign超时结论
- 使用
调用接口分两层,Feign
的调用和ribbon
的调用,所以hystrix
的超时时间和ribbon
的超时时间的结合就是Hystrix
的超时时间Feign
- 一般情况下 都是
(<)ribbon的超时时间
(因为涉及到ribbon的重试机制),如果ribbon的超时时间大于hystrix的超时时间
的超时时间,对于hystrix
的重试是没有意义的(ribbon
超时熔断了,hystrix
无法重试),默认我项目中是没有配置ribbon
的重试的,但是需要注意的是无论是否需要配置ribbon超时时的重试次数,ribbon
的超时都是需要大于hystrix
的超时。ribbon
7.2.ribbon的重试机制
ribbon:
OkToRetryOnAllOperations: false #对所有操作请求都进行重试,默认false
ReadTimeout: 5000 #负载均衡超时时间,默认值5000
ConnectTimeout: 2000 #ribbon请求连接的超时时间,默认值2000
MaxAutoRetries: 0 #对当前实例的重试次数,默认0
MaxAutoRetriesNextServer: 1 #对切换实例的重试次数,默认1
hystrix:
command:
default: #default全局有效,service id指定应用有效
execution:
timeout:
enabled: true
isolation:
thread:
timeoutInMilliseconds: 10000 #断路器超时时间,默认1000ms