(點選上方公眾號,可快速關註)
來源:大名Dean鼎,
www.deanwangpro.com/2018/04/13/zuul-hytrix-ribbon-timeout/
大家在初次使用spring-cloud的gateway的時候,肯定會被裡面各種的Timeout搞得暈頭轉向。hytrix有設定,ribbon也有。我們一開始也是亂設一桶,Github上各種專案裡也沒幾個設定正確的。對Timeout的研究源於一次log中的warning
The Hystrix timeout of 60000 ms for the command “foo” is set lower than the combination of the Ribbon read and connect timeout, 200000ms.
hytrix超時時間
log出自AbstractRibbonCommand.java,那麼索性研究一下原始碼。
假設:
-
這裡gateway會請求一個serviceName=foo的服務
protected static int getHystrixTimeout(IClientConfig config, String commandKey) {
int ribbonTimeout = getRibbonTimeout(config, commandKey);
DynamicPropertyFactory dynamicPropertyFactory = DynamicPropertyFactory.getInstance();
// 獲取預設的hytrix超時時間
int defaultHystrixTimeout = dynamicPropertyFactory.getIntProperty(“hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds”,
0).get();
// 獲取具體服務的hytrix超時時間,這裡應該是hystrix.command.foo.execution.isolation.thread.timeoutInMilliseconds
int commandHystrixTimeout = dynamicPropertyFactory.getIntProperty(“hystrix.command.” + commandKey + “.execution.isolation.thread.timeoutInMilliseconds”,
0).get();
int hystrixTimeout;
// hystrixTimeout的優先順序是 具體服務的hytrix超時時間 > 預設的hytrix超時時間 > ribbon超時時間
if(commandHystrixTimeout > 0) {
hystrixTimeout = commandHystrixTimeout;
}
else if(defaultHystrixTimeout > 0) {
hystrixTimeout = defaultHystrixTimeout;
} else {
hystrixTimeout = ribbonTimeout;
}
// 如果預設的或者具體服務的hytrix超時時間小於ribbon超時時間就會警告
if(hystrixTimeout < ribbonTimeout) {
LOGGER.warn(“The Hystrix timeout of ” + hystrixTimeout + “ms for the command ” + commandKey +
” is set lower than the combination of the Ribbon read and connect timeout, ” + ribbonTimeout + “ms.”);
}
return hystrixTimeout;
}
緊接著,看一下我們的配置是什麼
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 60000
ribbon:
ReadTimeout: 50000
ConnectTimeout: 50000
MaxAutoRetries: 0
MaxAutoRetriesNextServer: 1
ribbon超時時間
這裡ribbon的超時時間是50000ms,那麼為什麼log中寫的ribbon時間是200000ms?
繼續分析原始碼:
protected static int getRibbonTimeout(IClientConfig config, String commandKey) {
int ribbonTimeout;
// 這是比較異常的情況,不說
if (config == null) {
ribbonTimeout = RibbonClientConfiguration.DEFAULT_READ_TIMEOUT + RibbonClientConfiguration.DEFAULT_CONNECT_TIMEOUT;
} else {
// 這裡獲取了四個引數,ReadTimeout,ConnectTimeout,MaxAutoRetries, MaxAutoRetriesNextServer
int ribbonReadTimeout = getTimeout(config, commandKey, “ReadTimeout”,
IClientConfigKey.Keys.ReadTimeout, RibbonClientConfiguration.DEFAULT_READ_TIMEOUT);
int ribbonConnectTimeout = getTimeout(config, commandKey, “ConnectTimeout”,
IClientConfigKey.Keys.ConnectTimeout, RibbonClientConfiguration.DEFAULT_CONNECT_TIMEOUT);
int maxAutoRetries = getTimeout(config, commandKey, “MaxAutoRetries”,
IClientConfigKey.Keys.MaxAutoRetries, DefaultClientConfigImpl.DEFAULT_MAX_AUTO_RETRIES);
int maxAutoRetriesNextServer = getTimeout(config, commandKey, “MaxAutoRetriesNextServer”,
IClientConfigKey.Keys.MaxAutoRetriesNextServer, DefaultClientConfigImpl.DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER);
// 原來ribbonTimeout的計算方法在這裡,以上文的設定為例
// ribbonTimeout = (50000 + 50000) * (0 + 1) * (1 + 1) = 200000
ribbonTimeout = (ribbonReadTimeout + ribbonConnectTimeout) * (maxAutoRetries + 1) * (maxAutoRetriesNextServer + 1);
}
return ribbonTimeout;
}
可以看到ribbonTimeout是一個總時間,所以從邏輯上來講,作者希望hystrixTimeout要大於ribbonTimeout,否則hystrix熔斷了以後,ribbon的重試就都沒有意義了。
ribbon單服務設定
到這裡最前面的疑問已經解開了,但是hytrix可以分服務設定timeout,ribbon可不可以? 原始碼走起,這裡看的檔案是DefaultClientConfigImpl.java
// 這是獲取配置的入口方法,如果是null,那麼用預設值
// 所有ribbon的預設值的都在該類中設定了,可以自己看一下
public
T get(IClientConfigKey key, T defaultValue) { T value = get(key);
if (value == null) {
value = defaultValue;
}
return value;
}
// 這是核心方法
protected Object getProperty(String key) {
if (enableDynamicProperties) {
String dynamicValue = null;
DynamicStringProperty dynamicProperty = dynamicProperties.get(key);
// dynamicProperties其實是一個快取,首次訪問foo服務的時候會載入
if (dynamicProperty != null) {
dynamicValue = dynamicProperty.get();
}
// 如果快取沒有,那麼就再獲取一次,註意這裡的getConfigKey(key)是生成key的方法
if (dynamicValue == null) {
dynamicValue = DynamicProperty.getInstance(getConfigKey(key)).getString();
// 如果還是沒有取預設值,getDefaultPropName(key)生成key的方法
if (dynamicValue == null) {
dynamicValue = DynamicProperty.getInstance(getDefaultPropName(key)).getString();
}
}
if (dynamicValue != null) {
return dynamicValue;
}
}
return properties.get(key);
}
以我們的服務為例:
getConfigKey(key) returns foo.ribbon.ReadTimeout
getDefaultPropName(key) returns ribbon.ReadTimeout
一目瞭然,{serviceName}.ribbon.{propertyName}就可以了。
小結
感覺ribbon和hytrix的配置獲取原始碼略微有點亂,所以也導致大家在設定的時候有些無所適從。spring-cloud的程式碼一直在迭代,無論github上還是檔案可能都相對滯後,這時候閱讀原始碼並且動手debug一下是最能接近事實真相的了。
看完本文有收穫?請轉發分享給更多人
關註「ImportNew」,提升Java技能