diff --git a/docs/src/main/asciidoc/spring-cloud-netflix.adoc b/docs/src/main/asciidoc/spring-cloud-netflix.adoc index 08037e1c91..1f2e60d272 100755 --- a/docs/src/main/asciidoc/spring-cloud-netflix.adoc +++ b/docs/src/main/asciidoc/spring-cloud-netflix.adoc @@ -112,6 +112,19 @@ NOTE: Because of a limitation in Eureka, it is not possible to support per-serve If you want to customize the RestTemplate used by the Eureka HTTP Client you may want to create a bean of `EurekaClientHttpRequestFactorySupplier` and provide your own logic for generating a `ClientHttpRequestFactory` instance. +All default timeout-related properties for RestTemplate used by the Eureka HTTP Client are set to infinite. Therefore, to specify the timeout values, you must specify the value directly with the properties in `eureka.client.rest-template-timeout`. (All timeout properties are in milliseconds.) + +.application.yml +[source,yaml] +---- +eureka: + client: + rest-template-timeout: + connect-timeout: 5000 + connect-request-timeout: 8000 + socket-timeout: 10000 +---- + === Status Page and Health Indicator The status page and health indicators for a Eureka instance default to `/info` and `/health` respectively, which are the default locations of useful endpoints in a Spring Boot Actuator application. diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/RestTemplateTimeoutProperties.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/RestTemplateTimeoutProperties.java new file mode 100644 index 0000000000..df04c295a6 --- /dev/null +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/RestTemplateTimeoutProperties.java @@ -0,0 +1,96 @@ +/* + * Copyright 2013-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.netflix.eureka; + +import java.util.Objects; + +import org.apache.http.client.config.RequestConfig; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.cloud.netflix.eureka.http.RestTemplateEurekaHttpClient; +import org.springframework.web.client.RestTemplate; + +/** + * Properties for configuring timeouts used in {@link RestTemplate} required by + * {@link RestTemplateEurekaHttpClient}. + * + * @author Jiwon Jeon + * @since 4.0.0 + */ +@ConfigurationProperties("eureka.client.rest-template-timeout") +public class RestTemplateTimeoutProperties { + + /** + * Default values are set to -1 according to {@link RequestConfig .DEFAULT}. + */ + private int connectTimeout = -1; + + private int connectRequestTimeout = -1; + + private int socketTimeout = -1; + + public int getConnectTimeout() { + return connectTimeout; + } + + public int getConnectRequestTimeout() { + return connectRequestTimeout; + } + + public int getSocketTimeout() { + return socketTimeout; + } + + public void setConnectTimeout(int connectTimeout) { + this.connectTimeout = connectTimeout; + } + + public void setConnectRequestTimeout(int connectRequestTimeout) { + this.connectRequestTimeout = connectRequestTimeout; + } + + public void setSocketTimeout(int socketTimeout) { + this.socketTimeout = socketTimeout; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + RestTemplateTimeoutProperties that = (RestTemplateTimeoutProperties) o; + + return connectTimeout == that.connectTimeout && connectRequestTimeout == that.connectRequestTimeout + && socketTimeout == that.socketTimeout; + } + + @Override + public int hashCode() { + return Objects.hash(connectTimeout, connectRequestTimeout, socketTimeout); + } + + @Override + public String toString() { + return "RestTemplateTimeoutProperties{" + ", connectTimeout=" + connectTimeout + ", connectRequestTimeout=" + + connectRequestTimeout + ", socketTimeout=" + socketTimeout + '}'; + } + +} diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/DiscoveryClientOptionalArgsConfiguration.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/DiscoveryClientOptionalArgsConfiguration.java index 29c8cf5e2d..42d689858c 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/DiscoveryClientOptionalArgsConfiguration.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/DiscoveryClientOptionalArgsConfiguration.java @@ -31,9 +31,11 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.configuration.SSLContextFactory; import org.springframework.cloud.configuration.TlsProperties; import org.springframework.cloud.netflix.eureka.MutableDiscoveryClientOptionalArgs; +import org.springframework.cloud.netflix.eureka.RestTemplateTimeoutProperties; import org.springframework.cloud.netflix.eureka.http.DefaultEurekaClientHttpRequestFactorySupplier; import org.springframework.cloud.netflix.eureka.http.EurekaClientHttpRequestFactorySupplier; import org.springframework.cloud.netflix.eureka.http.RestTemplateDiscoveryClientOptionalArgs; @@ -46,6 +48,7 @@ * @author Daniel Lavoie */ @Configuration(proxyBeanMethods = false) +@EnableConfigurationProperties(RestTemplateTimeoutProperties.class) public class DiscoveryClientOptionalArgsConfiguration { protected static final Log logger = LogFactory.getLog(DiscoveryClientOptionalArgsConfiguration.class); @@ -75,8 +78,9 @@ public RestTemplateDiscoveryClientOptionalArgs restTemplateDiscoveryClientOption @Bean @ConditionalOnMissingBean @ConditionalOnClass(name = "org.springframework.web.client.RestTemplate") - EurekaClientHttpRequestFactorySupplier defaultEurekaClientHttpRequestFactorySupplier() { - return new DefaultEurekaClientHttpRequestFactorySupplier(); + EurekaClientHttpRequestFactorySupplier defaultEurekaClientHttpRequestFactorySupplier( + RestTemplateTimeoutProperties restTemplateTimeoutProperties) { + return new DefaultEurekaClientHttpRequestFactorySupplier(restTemplateTimeoutProperties); } @Bean diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/DefaultEurekaClientHttpRequestFactorySupplier.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/DefaultEurekaClientHttpRequestFactorySupplier.java index 7762f14cc8..ad3e40e9ee 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/DefaultEurekaClientHttpRequestFactorySupplier.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/DefaultEurekaClientHttpRequestFactorySupplier.java @@ -19,10 +19,12 @@ import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; +import org.apache.http.client.config.RequestConfig; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; +import org.springframework.cloud.netflix.eureka.RestTemplateTimeoutProperties; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.lang.Nullable; @@ -36,6 +38,16 @@ */ public class DefaultEurekaClientHttpRequestFactorySupplier implements EurekaClientHttpRequestFactorySupplier { + private final RestTemplateTimeoutProperties restTemplateTimeoutProperties; + + public DefaultEurekaClientHttpRequestFactorySupplier() { + this.restTemplateTimeoutProperties = new RestTemplateTimeoutProperties(); + } + + public DefaultEurekaClientHttpRequestFactorySupplier(RestTemplateTimeoutProperties restTemplateTimeoutProperties) { + this.restTemplateTimeoutProperties = restTemplateTimeoutProperties; + } + @Override public ClientHttpRequestFactory get(SSLContext sslContext, @Nullable HostnameVerifier hostnameVerifier) { HttpClientBuilder httpClientBuilder = HttpClients.custom(); @@ -45,10 +57,20 @@ public ClientHttpRequestFactory get(SSLContext sslContext, @Nullable HostnameVer if (hostnameVerifier != null) { httpClientBuilder = httpClientBuilder.setSSLHostnameVerifier(hostnameVerifier); } + if (restTemplateTimeoutProperties != null) { + httpClientBuilder.setDefaultRequestConfig(buildRequestConfig()); + } + CloseableHttpClient httpClient = httpClientBuilder.build(); HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); requestFactory.setHttpClient(httpClient); return requestFactory; } + private RequestConfig buildRequestConfig() { + return RequestConfig.custom().setConnectTimeout(restTemplateTimeoutProperties.getConnectTimeout()) + .setConnectionRequestTimeout(restTemplateTimeoutProperties.getConnectRequestTimeout()) + .setSocketTimeout(restTemplateTimeoutProperties.getSocketTimeout()).build(); + } + }