【Spring Cloud 源码解读】之 【如何配置好OpenFeign的各类超时时间!】

关于Feign的超时详解:

Spring Cloud微服务架构中,大部分公司都是利用Open Feign进行服务间的调用,而比较简单的业务使用默认配置是不会有多大问题的,可是若是是业务比较复杂,服务要进行比较繁杂的业务计算,那后台颇有可能会出现Read Timeout这个异常。html

一、关于hystrix的熔断超时

若是Feign开启了熔断,必需要从新设置熔断超时的时间,由于默认的熔断超时时间过短了,只有1秒,这容易致使业务服务的调用还没完成而后超时就被熔断了。java

如何配置熔断超时:spring

#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

<font color="red">注意:关于hystrixapplication.properties配置是没提示的,可是HystrixCommandProperties是会获取的。</font>缓存

// 构造函数
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();
}

二、关于Ribbon超时。

Feign调用默认是使用Ribbon进行负载均衡的,因此咱们还须要了解关于Ribbon的超时。架构

①、Feign的调用链路

看一下Feign的请求是否有使用Ribbon的超时时间,并且是如何读取Ribbon的超时时间的?app

(1)、org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient#execute负载均衡

(2)、com.netflix.client.AbstractLoadBalancerAwareClient#executeWithLoadBalancer(S, com.netflix.client.config.IClientConfig)ide

(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

总结

如何配置好HystrixRibbon的超时时间呢? 实际上是有套路的。由于Feign的请求:实际上是Hystrix+RibbonHystrix在最外层,而后再到Ribbon,最后里面的是http请求。因此说。Hystrix的熔断时间必须大于Ribbon的 ( ConnectTimeout + ReadTimeout )。而若是Ribbon开启了重试机制,还须要乘以对应的重试次数,保证在Ribbon里的请求还没结束时,Hystrix的熔断时间不会超时。

原文出处:https://www.cnblogs.com/Howinfun/p/12149982.html

相关文章
相关标签/搜索