序
本文主要研究一下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
- Client Side Load Balancing with Ribbon and Spring Cloud