Talk about ribbon timeout settings

Posted by Jason Batten on Fri, 07 Feb 2020 19:28:37 +0100

order

This article mainly studies ribbon timeout settings

To configure

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

	//......
}
  • Set the default timeout here, which is 1000ms. Set the 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 RibbonProperties and placed into the class member variables connectTimeout and readTimeout
  • RibbonProperties is last 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, setting 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, OkHttpClient with the specified timeout parameter is constructed by configOverride or default config
  • OkHttpClient is set by client rather than apache httpclient by request config. There may be a problem that OkHttpClient cannot use a singleton and has to have a new one each 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);
		}
	}

	//......
}
  • Here client.executeWithLoadBalancer(request, config) uses the config configuration of 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 obtained by RibbonClientHttpRequestFactory from serviceId. DefaultClientConfigImpl is the default. Read from the configuration file, serviceId's own personalized configuration parameters override the default values. What it cannot read is the default parameters.

Summary

The ribbon for spring cloud netflix has ReadTimeout and ConnectTimeout configured with timeout, socketTimeout and ConnectTimeout set respectively. When a request is created, the specified configuration is read. If not, the default configuration is used to set the timeout.

doc

Topics: Spring Java Apache github