歡迎光臨
每天分享高質量文章

spring-cloud 服務閘道器中的 Timeout 設定

(點選上方公眾號,可快速關註)


來源:大名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技能

贊(0)

分享創造快樂