Feign的各种超时时间(含局部方法设置超时案例)
关于Feign的超时记录:在Spring Cloud微服务架构中,大部分公司都是利用Open Feign进行服务间的调用,而比较简单的业务使用默认配置是不会有多大问题的,但是如果是业务比较复杂,服务要进行比较繁杂的业务计算,那后台很有可能会出现Read Timeout这个异常。1、关于hystrix的熔断超时如果Feign开启了熔断,必须要重新设置熔断超时的时间,因为默认的熔断超时时间...
关于Feign的超时记录:
在Spring Cloud
微服务架构中,大部分公司都是利用Open Feign
进行服务间的调用,而比较简单的业务使用默认配置是不会有多大问题的,但是如果是业务比较复杂,服务要进行比较繁杂的业务计算,那后台很有可能会出现Read Timeout
这个异常。
1、关于hystrix的熔断超时
如果Feign
开启了熔断,必须要重新设置熔断超时的时间,因为默认的熔断超时时间太短了,只有1秒,这容易导致业务服务的调用还没完成然后超时就被熔断了。
如何配置熔断超时:
-
#Feign如何开启熔断
-
feign.hystrix.enabled=true
-
#是否开始超时熔断,如果为false,则熔断机制只在服务不可用时开启(spring-cloud-starter-openfeign中的HystrixCommandProperties默认为true)
-
hystrix.command.default.execution.timeout.enabled=true
-
#设置超时熔断时间(spring-cloud-starter-openfeign中的HystrixCommandProperties默认为1000毫秒)
-
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=6000
注意:关于hystrix
在application.properties
配置是没提示的,但是HystrixCommandProperties
是会获取的。
-
// 构造函数
-
protected HystrixCommandProperties(HystrixCommandKey key, HystrixCommandProperties.Setter builder, String propertyPrefix) {
-
// .... 省略很多其他配置
-
// propertyPrefix:hystrix,key:default
-
this.executionTimeoutInMilliseconds = getProperty(propertyPrefix, key, "execution.isolation.thread.timeoutInMilliseconds", builder.getExecutionIsolationThreadTimeoutInMilliseconds(), default_executionTimeoutInMilliseconds);
-
}
-
// 具体获取属性的方法
-
private static HystrixProperty<String> getProperty(String propertyPrefix, HystrixCommandKey key, String instanceProperty, String builderOverrideValue, String defaultValue) {
-
return HystrixPropertiesChainedProperty.forString().add(propertyPrefix + ".command." + key.name() + "." + instanceProperty, builderOverrideValue).add(propertyPrefix + ".command.default." + instanceProperty, defaultValue).build();
-
}
2、Feign局部设置超时间
spring-cloud-dependencies Dalston版本之后,默认Feign对Hystrix的支持默认是关闭的,需要手动开启。
feign.hystrix.enabled=true
开启hystrix,可以选择关闭熔断或超时。
2.1关闭熔断:
-
# 全局关闭熔断:
-
hystrix.command.default.circuitBreaker.enabled: false
-
# 局部关闭熔断:
-
hystrix.command.<HystrixCommandKey>.circuitBreaker.enabled: false
2.2设置超时:
-
# 全局设置超时:
-
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 1000
-
# 局部设置超时:
-
hystrix.command.<HystrixCommandKey>.execution.isolation.thread.timeoutInMilliseconds: 1000
2.3设置局部方法超时成功案例:
需要对下图两个方法局部设置超时时间为10秒,
方法一、配置如下:
-
# 禁用Hystrix超时
-
hystrix.threadpool.default.coreSize = 10
-
hystrix.command.default.fallback.enabled = true
-
hystrix.command.default.execution.timeout.enabled= true
-
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds = 3000
-
hystrix.command.MemberStaffFeign#getExcelDataByDoctor(String,Integer,Integer).execution.isolation.thread.timeoutInMilliseconds=10000
-
hystrix.command.MemberStaffFeign#getExcelDataByTeam(String,Integer,Integer).execution.isolation.thread.timeoutInMilliseconds=10000
@SpringBootApplication启动类中添加@EnableCircuitBreaker注解
-
@EnableFeignClients
-
@EnableEurekaClient
-
@EnableDiscoveryClient
-
@EnableApolloConfig
-
@EnableCircuitBreaker
-
@SpringBootApplication
-
public class BusinessSystemApplication {
-
public static void main(String[] args) {
-
ConfigurableApplicationContext app = new SpringApplicationBuilder(BusinessSystemApplication.class).run(args);
-
System.out.println(app.getEnvironment().getProperty("spring.application.name") + "服务启动完毕...");
-
}
-
}
2.4关闭超时
-
# 全局关闭:
-
hystrix.command.default.execution.timeout.enabled: false
-
# 局部关闭:
-
hystrix.command.<HystrixCommandKey>.execution.timeout.enabled: false
3、关于Ribbon超时。
Feign
调用默认是使用Ribbon
进行负载均衡的,所以我们还需要了解关于Ribbon
的超时。
①、Feign的调用链路
看一下Feign的请求是否有使用Ribbon的超时时间,而且是如何读取Ribbon的超时时间的?
(1)、org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient#execute
(2)、com.netflix.client.AbstractLoadBalancerAwareClient#executeWithLoadBalancer(S, com.netflix.client.config.IClientConfig)
(3)、org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory#create
-
创建Client,这里会判断对应ClientName的链接Client是否创建过,如果创建过复用之前的Client;
-
如果不存在则创建一个并且放入cache缓存。
-
public FeignLoadBalancer create(String clientName) {
-
FeignLoadBalancer client = this.cache.get(clientName);
-
if(client != null) {
-
return client;
-
}
-
IClientConfig config = this.factory.getClientConfig(clientName);
-
ILoadBalancer lb = this.factory.getLoadBalancer(clientName);
-
ServerIntrospector serverIntrospector = this.factory.getInstance(clientName, ServerIntrospector.class);
-
// 判断是否有重试
-
client = loadBalancedRetryFactory != null ? new RetryableFeignLoadBalancer(lb, config, serverIntrospector,
-
loadBalancedRetryFactory) : new FeignLoadBalancer(lb, config, serverIntrospector);
-
this.cache.put(clientName, client);
-
return client;
-
}
(4)、com.netflix.client.AbstractLoadBalancerAwareClient#executeWithLoadBalancer(S, com.netflix.client.config.IClientConfig)
负载均衡器抽象类
(5)、org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer#execute
-
Feign的负载均衡器实现类。到这里我们可以看到,连接超时和读超时的配置都在这里:
-
如果application.properties配置文件中的超时时间不为空,则使用配置的超时时间。
-
如果为空则使用默认值,而从FeignLoadBalancer的构造函数可以看到,默认值也是取的RibbonProperties的默认超时时间。
-
public RibbonResponse execute(RibbonRequest request, IClientConfig configOverride)
-
throws IOException {
-
Request.Options options;
-
// 设置超时时间。,如果orride的配置为空,则用默认值
-
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);
-
}
-
// 发起请求
-
Response response = request.client().execute(request.toRequest(), options);
-
return new RibbonResponse(request.getUri(), response);
-
}
-
// 构造函数
-
public FeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig, ServerIntrospector serverIntrospector) {
-
super(lb, clientConfig);
-
this.setRetryHandler(RetryHandler.DEFAULT);
-
this.clientConfig = clientConfig;
-
this.ribbon = RibbonProperties.from(clientConfig);
-
RibbonProperties ribbon = this.ribbon;
-
this.connectTimeout = ribbon.getConnectTimeout();
-
this.readTimeout = ribbon.getReadTimeout();
-
this.serverIntrospector = serverIntrospector;
-
}
②、Ribbon的默认超时时间
在RibbonClientConfiguration
中:
-
public static final int DEFAULT_CONNECT_TIMEOUT = 1000;
-
public static final int DEFAULT_READ_TIMEOUT = 1000;
③、如何自定义Ribbon超时时间
首先,RibbonProperties
的超时时间的读取的源码如下:
-
public Integer getConnectTimeout() {
-
return (Integer)this.get(CommonClientConfigKey.ConnectTimeout);
-
}
-
public Integer getReadTimeout() {
-
return (Integer)this.get(CommonClientConfigKey.ReadTimeout);
-
}
然后,可以在CommonClientConfigKey
中可以看到两个超时时间的名称:
-
// ConnectTimeout:
-
public static final IClientConfigKey<Integer> ConnectTimeout = new CommonClientConfigKey<Integer>("ConnectTimeout") {};
-
// ReadTimeout:
-
public static final IClientConfigKey<Integer> ReadTimeout = new CommonClientConfigKey<Integer>("ReadTimeout") {};
然后,在IClientConfig
的默认实现类:DefaultClientConfigImpl
中,可以发现Ribbon
配置的前缀
public static final String DEFAULT_PROPERTY_NAME_SPACE = "ribbon";
所以,最后Ribbon
该这么配置超时时间:
-
ribbon.ConnectTimeout=5000
-
ribbon.ReadTimeout=5000
总结
1.如何配置好Hystrix
和Ribbon
的超时时间呢?
其实是有套路的,因为Feign
的请求:其实是Hystrix
+Ribbon
。Hystrix
在最外层,然后再到Ribbon
,最后里面的是http
请求。所以说。Hystrix
的熔断时间必须大于Ribbon
的 ( ConnectTimeout
+ ReadTimeout
)。而如果Ribbon
开启了重试机制,还需要乘以对应的重试次数,保证在Ribbon
里的请求还没结束时,Hystrix
的熔断时间不会超时。
更多推荐
所有评论(0)