【Spring Cloud 源碼解讀】之 【如何配置好OpenFeign的各種超時時間!】


關於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

注意:關於hystrixapplication.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、關於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

總結

如何配置好HystrixRibbon的超時時間呢?
其實是有套路的。因為Feign的請求:其實是Hystrix+RibbonHystrix在最外層,然后再到Ribbon,最后里面的是http請求。所以說。Hystrix的熔斷時間必須大於Ribbon的 ( ConnectTimeout + ReadTimeout )。而如果Ribbon開啟了重試機制,還需要乘以對應的重試次數,保證在Ribbon里的請求還沒結束時,Hystrix的熔斷時間不會超時。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM