本文主要研究一下ribbon的超时时间设置

配置

实例

ribbon:
  ReadTimeout: 10000
  ConnectTimeout: 10000
  MaxAutoRetries: 0
  MaxAutoRetriesNextServer: 1
  eureka:
    enabled: true

RibbonClientConfiguration

spring-cloud-netflix-ribbon-2.0.0.RELEASE-sources.jar!/org/springframework/cloud/netflix/ribbon/RibbonClientConfiguration.java

@SuppressWarnings("deprecation")
@Configuration
@EnableConfigurationProperties
//Order is important here, last should be the default, first should be optional
// see https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
@Import({HttpClientConfiguration.class, OkHttpRibbonConfiguration.class, RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class})
public class RibbonClientConfiguration {

    public static final int DEFAULT_CONNECT_TIMEOUT = 1000;
    public static final int DEFAULT_READ_TIMEOUT = 1000;

    @RibbonClientName
    private String name = "client";

    // TODO: maybe re-instate autowired load balancers: identified by name they could be
    // associated with ribbon clients

    @Autowired
    private PropertiesFactory propertiesFactory;

    @Bean
    @ConditionalOnMissingBean
    public IClientConfig ribbonClientConfig() {
        DefaultClientConfigImpl config = new DefaultClientConfigImpl();
        config.loadProperties(this.name);
        config.set(CommonClientConfigKey.ConnectTimeout, DEFAULT_CONNECT_TIMEOUT);
        config.set(CommonClientConfigKey.ReadTimeout, DEFAULT_READ_TIMEOUT);
        return config;
    }

    //......
}
  • 这里设置默认的超时值,都是1000毫秒,设置在DefaultClientConfigImpl

AbstractLoadBalancingClient

spring-cloud-netflix-ribbon-2.0.0.RELEASE-sources.jar!/org/springframework/cloud/netflix/ribbon/support/AbstractLoadBalancingClient.java

public abstract class AbstractLoadBalancingClient<S extends ContextAwareRequest, T extends IResponse, D> extends
        AbstractLoadBalancerAwareClient<S, T> implements ServiceInstanceChooser {

    protected int connectTimeout;

    protected int readTimeout;

    //......

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
        super.initWithNiwsConfig(clientConfig);
        RibbonProperties ribbon = RibbonProperties.from(clientConfig);
        this.connectTimeout = ribbon.connectTimeout(DEFAULT_CONNECT_TIMEOUT);
        this.readTimeout = ribbon.readTimeout(DEFAULT_READ_TIMEOUT);
        this.secure = ribbon.isSecure();
        this.followRedirects = ribbon.isFollowRedirects();
        this.okToRetryOnAllOperations = ribbon.isOkToRetryOnAllOperations();
    }

    //......
}
  • 这里从RibbonProperties读取超时参数,然后放到类成员变量connectTimeout及readTimeout
  • RibbonProperties就最后是从IClientConfig读取

RibbonLoadBalancingHttpClient

spring-cloud-netflix-ribbon-2.0.0.RELEASE-sources.jar!/org/springframework/cloud/netflix/ribbon/apache/RibbonLoadBalancingHttpClient.java

// TODO: rename (ie new class that extends this in Dalston) to ApacheHttpLoadBalancingClient
public class RibbonLoadBalancingHttpClient extends
        AbstractLoadBalancingClient<RibbonApacheHttpRequest, RibbonApacheHttpResponse, CloseableHttpClient> {
    //......

    @Override
    public RibbonApacheHttpResponse execute(RibbonApacheHttpRequest request,
                                            final IClientConfig configOverride) throws Exception {
        IClientConfig config = configOverride != null ? configOverride : this.config;
        RibbonProperties ribbon = RibbonProperties.from(config);
        RequestConfig requestConfig = RequestConfig.custom()
                .setConnectTimeout(ribbon.connectTimeout(this.connectTimeout))
                .setSocketTimeout(ribbon.readTimeout(this.readTimeout))
                .setRedirectsEnabled(ribbon.isFollowRedirects(this.followRedirects))
                .build();

        request = getSecureRequest(request, configOverride);
        final HttpUriRequest httpUriRequest = request.toRequest(requestConfig);
        final HttpResponse httpResponse = this.delegate.execute(httpUriRequest);
        return new RibbonApacheHttpResponse(httpResponse, httpUriRequest.getURI());
    }

    //......
}
  • 这里execute方法从IClientConfig构造RequestConfig,会设置connectTimeout及socketTimeout
  • 如果configOverride为null,则使用抽象类的默认配置

OkHttpLoadBalancingClient

spring-cloud-netflix-ribbon-2.0.0.RELEASE-sources.jar!/org/springframework/cloud/netflix/ribbon/okhttp/OkHttpLoadBalancingClient.java

public class OkHttpLoadBalancingClient
        extends AbstractLoadBalancingClient<OkHttpRibbonRequest, OkHttpRibbonResponse, OkHttpClient> {

    //......

    @Override
    public OkHttpRibbonResponse execute(OkHttpRibbonRequest ribbonRequest,
                                        final IClientConfig configOverride) throws Exception {
        boolean secure = isSecure(configOverride);
        if (secure) {
            final URI secureUri = UriComponentsBuilder.fromUri(ribbonRequest.getUri())
                    .scheme("https").build().toUri();
            ribbonRequest = ribbonRequest.withNewUri(secureUri);
        }

        OkHttpClient httpClient = getOkHttpClient(configOverride, secure);
        final Request request = ribbonRequest.toRequest();
        Response response = httpClient.newCall(request).execute();
        return new OkHttpRibbonResponse(response, ribbonRequest.getUri());
    }

    OkHttpClient getOkHttpClient(IClientConfig configOverride, boolean secure) {
        IClientConfig config = configOverride != null ? configOverride : this.config;
        RibbonProperties ribbon = RibbonProperties.from(config);
        OkHttpClient.Builder builder = this.delegate.newBuilder()
                .connectTimeout(ribbon.connectTimeout(this.connectTimeout), TimeUnit.MILLISECONDS)
                .readTimeout(ribbon.readTimeout(this.readTimeout), TimeUnit.MILLISECONDS)
                .followRedirects(ribbon.isFollowRedirects(this.followRedirects));
        if (secure) {
            builder.followSslRedirects(ribbon.isFollowRedirects(this.followRedirects));
        }

        return builder.build();
    }

    //......
}
  • 这里是通过configOverride或默认的config来构建指定超时参数的OkHttpClient
  • 相比较于apache httpclient通过request config来设置超时时间,OkHttpClient是通过client来设置的,这样可能存在一个问题,就是OkHttpClient没法用单例,每次都得new一个

clientConfig传递

RibbonHttpRequest

spring-cloud-netflix-ribbon-2.0.0.RELEASE-sources.jar!/org/springframework/cloud/netflix/ribbon/RibbonHttpRequest.java

public class RibbonHttpRequest extends AbstractClientHttpRequest {
    //......

    @Override
    protected ClientHttpResponse executeInternal(HttpHeaders headers)
            throws IOException {
        try {
            addHeaders(headers);
            if (outputStream != null) {
                outputStream.close();
                builder.entity(outputStream.toByteArray());
            }
            HttpRequest request = builder.build();
            HttpResponse response = client.executeWithLoadBalancer(request, config);
            return new RibbonHttpResponse(response);
        } catch (Exception e) {
            throw new IOException(e);
        }
    }

    //......
}
  • 这里client.executeWithLoadBalancer(request, config)使用的是RibbonHttpRequest的config配置

RibbonClientHttpRequestFactory

spring-cloud-netflix-ribbon-2.0.0.RELEASE-sources.jar!/org/springframework/cloud/netflix/ribbon/RibbonClientHttpRequestFactory.java

public class RibbonClientHttpRequestFactory implements ClientHttpRequestFactory {

    private final SpringClientFactory clientFactory;

    public RibbonClientHttpRequestFactory(SpringClientFactory clientFactory) {
        this.clientFactory = clientFactory;
    }

    @Override
    @SuppressWarnings("deprecation")
    public ClientHttpRequest createRequest(URI originalUri, HttpMethod httpMethod)
            throws IOException {
        String serviceId = originalUri.getHost();
        if (serviceId == null) {
            throw new IOException(
                    "Invalid hostname in the URI [" + originalUri.toASCIIString() + "]");
        }
        IClientConfig clientConfig = this.clientFactory.getClientConfig(serviceId);
        RestClient client = this.clientFactory.getClient(serviceId, RestClient.class);
        HttpRequest.Verb verb = HttpRequest.Verb.valueOf(httpMethod.name());

        return new RibbonHttpRequest(originalUri, verb, client, clientConfig);
    }

}
  • ClientHttpRequest是通过RibbonClientHttpRequestFactory这个工厂创建的
  • clientConfig是RibbonClientHttpRequestFactory这个工厂根据serviceId获取的,默认是DefaultClientConfigImpl,从配置文件读取,serviceId自己的个性化配置参数会覆盖默认值,读取不到的就是默认的参数。

小结

spring cloud netflix的ribbon,其超时时间配置有ReadTimeout以及ConnectTimeout,分别是设置的socketTimeout以及connectTimeout,创建请求的时候,会读取指定配置,没有的话,就取默认的配置,设置超时时间。

doc


codecraft
11.9k 声望2k 粉丝

当一个代码的工匠回首往事时,不因虚度年华而悔恨,也不因碌碌无为而羞愧,这样,当他老的时候,可以很自豪告诉世人,我曾经将代码注入生命去打造互联网的浪潮之巅,那是个很疯狂的时代,我在一波波的浪潮上留下...