大家在初次使用spring-cloud的gateway時(shí),肯定會(huì)被里面各種的Timeout搞得暈頭轉(zhuǎn)向,hytrix有設(shè)置,ribbon也有徐鹤。我們一開始也是亂設(shè)一通,Github上各種項(xiàng)目里也沒幾個(gè)設(shè)置正確的邀层,對(duì)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超時(shí)時(shí)間
log出自AbstractRibbonCommand.java,那么索性研究一下源碼寥院。
假設(shè):
這里gateway會(huì)請(qǐng)求一個(gè)serviceName=foo的服務(wù)
protected static int getHystrixTimeout(IClientConfig config, String commandKey) {
? ? int ribbonTimeout = getRibbonTimeout(config, commandKey);
? ? DynamicPropertyFactory dynamicPropertyFactory = DynamicPropertyFactory.getInstance();
? ? // 獲取默認(rèn)的hytrix超時(shí)時(shí)間
? ? int defaultHystrixTimeout = dynamicPropertyFactory.getIntProperty("hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds",
? ? ? ? 0).get();
? ? // 獲取具體服務(wù)的hytrix超時(shí)時(shí)間劲赠,這里應(yīng)該是hystrix.command.foo.execution.isolation.thread.timeoutInMilliseconds
? ? int commandHystrixTimeout = dynamicPropertyFactory.getIntProperty("hystrix.command." + commandKey + ".execution.isolation.thread.timeoutInMilliseconds",
? ? ? ? 0).get();
? ? int hystrixTimeout;
? ? // hystrixTimeout的優(yōu)先級(jí)是 具體服務(wù)的hytrix超時(shí)時(shí)間 > 默認(rèn)的hytrix超時(shí)時(shí)間 > ribbon超時(shí)時(shí)間
? ? if(commandHystrixTimeout > 0) {
? ? ? ? hystrixTimeout = commandHystrixTimeout;
? ? }
? ? else if(defaultHystrixTimeout > 0) {
? ? ? ? hystrixTimeout = defaultHystrixTimeout;
? ? } else {
? ? ? ? hystrixTimeout = ribbonTimeout;
? ? }
? ? // 如果默認(rèn)的或者具體服務(wù)的hytrix超時(shí)時(shí)間小于ribbon超時(shí)時(shí)間就會(huì)警告
? ? 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超時(shí)時(shí)間
這里ribbon的超時(shí)時(shí)間是50000ms秸谢,那么為什么log中寫的ribbon時(shí)間是200000ms?
繼續(xù)分析源碼:
protected static int getRibbonTimeout(IClientConfig config, String commandKey) {
? ? int ribbonTimeout;
? ? // 這是比較異常的情況凛澎,不說
? ? if (config == null) {
? ? ? ? ribbonTimeout = RibbonClientConfiguration.DEFAULT_READ_TIMEOUT + RibbonClientConfiguration.DEFAULT_CONNECT_TIMEOUT;
? ? } else {
? ? ? ?// 這里獲取了四個(gè)參數(shù),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的計(jì)算方法在這里臭蚁,以上文的設(shè)置為例
? ? ? ? // ribbonTimeout = (50000 + 50000) * (0 + 1) * (1 + 1) = 200000
? ? ? ? ribbonTimeout = (ribbonReadTimeout + ribbonConnectTimeout) * (maxAutoRetries + 1) * (maxAutoRetriesNextServer + 1);
? ? }
? ? return ribbonTimeout;
}
可以看到ribbonTimeout是一個(gè)總時(shí)間最铁,所以從邏輯上來講,作者希望hystrixTimeout要大于ribbonTimeout垮兑,否則hystrix熔斷了以后冷尉,ribbon的重試就都沒有意義了。
ribbon單服務(wù)設(shè)置
到這里最前面的疑問已經(jīng)解開了系枪,但是hytrix可以分服務(wù)設(shè)置timeout雀哨,ribbon可不可以? 源碼走起,這里看的文件是DefaultClientConfigImpl.java
// 這是獲取配置的入口方法,如果是null雾棺,那么用默認(rèn)值
// 所有ribbon的默認(rèn)值的都在該類中設(shè)置了膊夹,可以自己看一下
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其實(shí)是一個(gè)緩存,首次訪問foo服務(wù)的時(shí)候會(huì)加載
? ? ? ? if (dynamicProperty != null) {
? ? ? ? ? ? dynamicValue = dynamicProperty.get();
? ? ? ? }
? ? ? ? // 如果緩存沒有垢村,那么就再獲取一次割疾,注意這里的getConfigKey(key)是生成key的方法
? ? ? ? if (dynamicValue == null) {
? ? ? ? ? ? dynamicValue = DynamicProperty.getInstance(getConfigKey(key)).getString();
? ? ? ? ? ? // 如果還是沒有取默認(rèn)值,getDefaultPropName(key)生成key的方法
? ? ? ? ? ? if (dynamicValue == null) {
? ? ? ? ? ? ? ? dynamicValue = DynamicProperty.getInstance(getDefaultPropName(key)).getString();
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? if (dynamicValue != null) {
? ? ? ? ? ? return dynamicValue;
? ? ? ? }
? ? }
? ? return properties.get(key);
}
以我們的服務(wù)為例:
getConfigKey(key) returns foo.ribbon.ReadTimeout
getDefaultPropName(key) returns ribbon.ReadTimeout
一目了然嘉栓,{serviceName}.ribbon.{propertyName}就可以了。
小結(jié)
感覺ribbon和hytrix的配置獲取源碼略微有點(diǎn)亂拓诸,所以也導(dǎo)致大家在設(shè)置的時(shí)候有些無所適從侵佃。spring-cloud的代碼一直在迭代,無論github上還是文檔可能都相對(duì)滯后奠支,這時(shí)候閱讀源碼并且動(dòng)手debug一下是最能接近事實(shí)真相的了馋辈。為了幫助大家少走彎路,我請(qǐng)BAT大廠工作的同事總結(jié)出一套技術(shù)視頻倍谜,涵蓋Java工程化迈螟、高性能及分布式、性能調(diào)優(yōu)尔崔、Spring答毫、Netty源碼分析和大數(shù)據(jù)等知識(shí)點(diǎn),大家可以通過掃碼進(jìn)群下載資料季春,其實(shí)我自己也比較喜歡技術(shù)洗搂,群里有一些阿里大牛,也有一線互聯(lián)網(wǎng)的資深HR载弄,最近在面試的朋友或者在找工作的可以進(jìn)來看看哦耘拇!