在Spring Cloud
微服務架構中,大部分公司都是利用Open Feign
進行服務間的調用,而比較簡單的業務使用默認配置是不會有多大問題的,可是若是是業務比較複雜,服務要進行比較繁雜的業務計算,那後臺頗有可能會出現Read Timeout
這個異常。html
若是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">注意:關於hystrix
在application.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(); }
Feign
調用默認是使用Ribbon
進行負載均衡的,因此咱們還須要瞭解關於Ribbon
的超時。架構
看一下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; }
在RibbonClientConfiguration
中:
public static final int DEFAULT_CONNECT_TIMEOUT = 1000; public static final int DEFAULT_READ_TIMEOUT = 1000;
首先,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
如何配置好Hystrix
和Ribbon
的超時時間呢? 實際上是有套路的。由於Feign
的請求:實際上是Hystrix
+Ribbon
。Hystrix
在最外層,而後再到Ribbon
,最後裏面的是http
請求。因此說。Hystrix
的熔斷時間必須大於Ribbon
的 ( ConnectTimeout
+ ReadTimeout
)。而若是Ribbon
開啓了重試機制,還須要乘以對應的重試次數,保證在Ribbon
裏的請求還沒結束時,Hystrix
的熔斷時間不會超時。
原文出處:https://www.cnblogs.com/Howinfun/p/12149982.html