Talk about the timeout setting for ribbon

  springcloud

Order

This article mainly studies the timeout setting of ribbon.

Configuration

Example

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;
    }

    //......
}
  • The default timeout values set here are all 1000 milliseconds and are set at 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();
    }

    //......
}
  • Here, the timeout parameter is read from the RibbonProperties and then put into the class member variables connectTimeout and readTimeout.
  • RibbonProperties was finally read from 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());
    }

    //......
}
  • Here, the execute method constructs RequestConfig from IClientConfig and sets connectTimeout and socketTimeout.
  • If configOverride is null, the default configuration of the abstract class is used.

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();
    }

    //......
}
  • Here is the OkHttpClient with the specified timeout parameter built through configOverride or the default config.
  • Compared with apache httpclient setting timeout time through request config, OkHttpclient is set through Client, so there may be a problem that OkHttpClient can’t use a single instance and needs new one at a time.

ClientConfig delivery

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);
        }
    }

    //......
}
  • Executewithloadbalancer (request, config) uses the config configuration of the RibbonHttpRequest.

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 was created through the RibbonClientHttpRequestFactory factory.
  • ClientConfig is acquired by the RibbonClientHttpRequestFactory according to the serviceId. The default value is DefaultClientConfigImpl. When read from the configuration file, the serviceId’s own personalized configuration parameters will override the default value, and those that cannot be read are the default parameters.

Summary

For the ribbon of spring cloud netflix, its timeout time is configured with ReadTimeout and connectTimeout, which are set socketTimeout and ConnectTimeout respectively. When creating a request, the specified configuration will be read. If not, the default configuration will be taken and the timeout time will be set.

doc