Skip to content

Commit

Permalink
Support Spring RestClient as default TransportClientFactory (#4281)
Browse files Browse the repository at this point in the history
  • Loading branch information
heowc authored Nov 4, 2024
1 parent 6b68d64 commit c2c5352
Show file tree
Hide file tree
Showing 24 changed files with 1,379 additions and 322 deletions.
6 changes: 4 additions & 2 deletions docs/modules/ROOT/pages/spring-cloud-netflix.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -270,9 +270,11 @@ It is initialized in a `SmartLifecycle` (with `phase=0`), so the earliest you ca

==== Underlying HTTP clients

`EurekaClient` uses either `RestTemplate`, `WebClient` or `JerseyClient` under the hood. In order to use the `EurekaClient`, you need to have one of the supported HTTP clients on your classpath.
`EurekaClient` uses either `RestClient`, `RestTemplate`, `WebClient` or `JerseyClient` under the hood. In order to use the `EurekaClient`, you need to have one of the supported HTTP clients on your classpath.

To use `RestTemplate`, add `spring-boot-starter-web` to your dependencies. To use `WebClient`, add `spring-boot-starter-webflux` to your dependencies. If both `RestTemplate` and `WebClient` are on the classpath when `eureka.client.webclient.enabled` is set to `true`, `WebClient` is used. Otherwise, `RestTemplate` is used.
To use `RestTemplate` or `RestClient`, add `spring-boot-starter-web` to your dependencies. To use `WebClient`, add `spring-boot-starter-webflux` to your dependencies. If both `spring-boot-starter-web` and `spring-boot-starter-webflux` are included in the dependencies and the `eureka.client.webclient.enabled` flag is set to `true`, then `WebClient` will be used. If that's not the case and `eureka.client.restclient.enabled` is set to false, the `RestTemplate` will be used. Otherwise, the `RestClient` will be used.

NOTE: Starting from 4.2.0, the default client has changed to `RestClient`.

If you wish to use Jersey instead, you need to add the Jersey dependencies to your classpath.
The following example shows the dependencies you need to add:
Expand Down
211 changes: 106 additions & 105 deletions docs/modules/ROOT/partials/_configprops.adoc

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
Expand All @@ -42,19 +41,23 @@
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.RestClientDiscoveryClientOptionalArgs;
import org.springframework.cloud.netflix.eureka.http.RestClientTransportClientFactories;
import org.springframework.cloud.netflix.eureka.http.RestTemplateDiscoveryClientOptionalArgs;
import org.springframework.cloud.netflix.eureka.http.RestTemplateTransportClientFactories;
import org.springframework.cloud.netflix.eureka.http.WebClientDiscoveryClientOptionalArgs;
import org.springframework.cloud.netflix.eureka.http.WebClientTransportClientFactories;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;

/**
* @author Daniel Lavoie
* @author Armin Krezovic
* @author Olga Maciaszek-Sharma
* @author Wonchul Heo
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(RestTemplateTimeoutProperties.class)
Expand All @@ -70,14 +73,14 @@ public TlsProperties tlsProperties() {

@Bean
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
@Conditional(JerseyClientNotPresentOrNotEnabledCondition.class)
@Conditional(RestTemplateEnabledCondition.class)
@ConditionalOnMissingBean(value = { AbstractDiscoveryClientOptionalArgs.class }, search = SearchStrategy.CURRENT)
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", matchIfMissing = true,
havingValue = "false")
public RestTemplateDiscoveryClientOptionalArgs restTemplateDiscoveryClientOptionalArgs(TlsProperties tlsProperties,
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier,
ObjectProvider<RestTemplateBuilder> restTemplateBuilders) throws GeneralSecurityException, IOException {
logger.info("Eureka HTTP Client uses RestTemplate.");
if (logger.isInfoEnabled()) {
logger.info("Eureka HTTP Client uses RestTemplate.");
}
RestTemplateDiscoveryClientOptionalArgs result = new RestTemplateDiscoveryClientOptionalArgs(
eurekaClientHttpRequestFactorySupplier, restTemplateBuilders::getIfAvailable);
setupTLS(result, tlsProperties);
Expand All @@ -86,10 +89,8 @@ public RestTemplateDiscoveryClientOptionalArgs restTemplateDiscoveryClientOption

@Bean
@ConditionalOnClass(name = "org.springframework.web.client.RestTemplate")
@Conditional(JerseyClientNotPresentOrNotEnabledCondition.class)
@Conditional(RestTemplateEnabledCondition.class)
@ConditionalOnMissingBean(value = { TransportClientFactories.class }, search = SearchStrategy.CURRENT)
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", matchIfMissing = true,
havingValue = "false")
public RestTemplateTransportClientFactories restTemplateTransportClientFactories(
RestTemplateDiscoveryClientOptionalArgs optionalArgs) {
return new RestTemplateTransportClientFactories(optionalArgs);
Expand Down Expand Up @@ -118,7 +119,9 @@ static class DiscoveryClientOptionalArgsTlsConfiguration {

DiscoveryClientOptionalArgsTlsConfiguration(TlsProperties tlsProperties,
AbstractDiscoveryClientOptionalArgs optionalArgs) throws GeneralSecurityException, IOException {
logger.info("Eureka HTTP Client uses Jersey");
if (logger.isInfoEnabled()) {
logger.info("Eureka HTTP Client uses Jersey");
}
setupTLS(optionalArgs, tlsProperties);
}

Expand All @@ -129,16 +132,15 @@ static class DiscoveryClientOptionalArgsTlsConfiguration {
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", havingValue = "true")
protected static class WebClientConfiguration {

@Autowired
private TlsProperties tlsProperties;

@Bean
@ConditionalOnMissingBean(
value = { AbstractDiscoveryClientOptionalArgs.class, RestTemplateDiscoveryClientOptionalArgs.class },
search = SearchStrategy.CURRENT)
public WebClientDiscoveryClientOptionalArgs webClientDiscoveryClientOptionalArgs(
public WebClientDiscoveryClientOptionalArgs webClientDiscoveryClientOptionalArgs(TlsProperties tlsProperties,
ObjectProvider<WebClient.Builder> builder) throws GeneralSecurityException, IOException {
logger.info("Eureka HTTP Client uses WebClient.");
if (logger.isInfoEnabled()) {
logger.info("Eureka HTTP Client uses WebClient.");
}
WebClientDiscoveryClientOptionalArgs result = new WebClientDiscoveryClientOptionalArgs(
builder::getIfAvailable);
setupTLS(result, tlsProperties);
Expand Down Expand Up @@ -168,6 +170,33 @@ public WebClientNotFoundConfiguration() {

}

@ConditionalOnClass(name = "org.springframework.web.client.RestClient")
@Conditional(RestClientEnabledCondition.class)
protected static class RestClientConfiguration {

@Bean
@ConditionalOnMissingBean(value = { AbstractDiscoveryClientOptionalArgs.class },
search = SearchStrategy.CURRENT)
public RestClientDiscoveryClientOptionalArgs restClientDiscoveryClientOptionalArgs(TlsProperties tlsProperties,
ObjectProvider<RestClient.Builder> builder) throws GeneralSecurityException, IOException {
if (logger.isInfoEnabled()) {
logger.info("Eureka HTTP Client uses RestClient.");
}
RestClientDiscoveryClientOptionalArgs result = new RestClientDiscoveryClientOptionalArgs(
builder::getIfAvailable);
setupTLS(result, tlsProperties);
return result;
}

@Bean
@ConditionalOnMissingBean(value = TransportClientFactories.class, search = SearchStrategy.CURRENT)
public RestClientTransportClientFactories restClientTransportClientFactories(
ObjectProvider<RestClient.Builder> builder) {
return new RestClientTransportClientFactories(builder::getIfAvailable);
}

}

static class JerseyClientPresentAndEnabledCondition extends AllNestedConditions {

JerseyClientPresentAndEnabledCondition() {
Expand Down Expand Up @@ -204,4 +233,53 @@ static class OnJerseyClientDisabled {

}

static class RestTemplateEnabledCondition extends AllNestedConditions {

RestTemplateEnabledCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}

@Conditional(JerseyClientNotPresentOrNotEnabledCondition.class)
static class OnJerseyClientNotPresentOrNotEnabled {

}

@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", matchIfMissing = true,
havingValue = "false")
static class OnWebClientDisabled {

}

@ConditionalOnProperty(prefix = "eureka.client", name = "restclient.enabled", havingValue = "false")
static class OnRestClientDisabled {

}

}

static class RestClientEnabledCondition extends AllNestedConditions {

RestClientEnabledCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}

@Conditional(JerseyClientNotPresentOrNotEnabledCondition.class)
static class OnJerseyClientNotPresentOrNotEnabled {

}

@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", matchIfMissing = true,
havingValue = "false")
static class OnWebClientDisabled {

}

@ConditionalOnProperty(prefix = "eureka.client", name = "restclient.enabled", matchIfMissing = true,
havingValue = "true")
static class OnRestClientDisabled {

}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
import org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration;
import org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration;
import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.client.RestTemplateBuilder;
Expand All @@ -37,6 +38,8 @@
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.RestClientEurekaHttpClient;
import org.springframework.cloud.netflix.eureka.http.RestClientTransportClientFactory;
import org.springframework.cloud.netflix.eureka.http.RestTemplateEurekaHttpClient;
import org.springframework.cloud.netflix.eureka.http.RestTemplateTransportClientFactory;
import org.springframework.cloud.netflix.eureka.http.WebClientEurekaHttpClient;
Expand All @@ -46,6 +49,7 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.lang.Nullable;
import org.springframework.web.client.RestClient;
import org.springframework.web.reactive.function.client.WebClient;

/**
Expand All @@ -54,6 +58,7 @@
*
* @author Dave Syer
* @author Armin Krezovic
* @author Wonchul Heo
*/
@ConditionalOnClass(ConfigServicePropertySourceLocator.class)
@Conditional(EurekaConfigServerBootstrapConfiguration.EurekaConfigServerBootstrapCondition.class)
Expand All @@ -69,8 +74,7 @@ public EurekaClientConfigBean eurekaClientConfigBean() {

@Bean
@ConditionalOnMissingBean(EurekaHttpClient.class)
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", matchIfMissing = true,
havingValue = "false")
@Conditional(RestTemplateEnabledCondition.class)
public RestTemplateEurekaHttpClient configDiscoveryRestTemplateEurekaHttpClient(EurekaClientConfigBean config,
Environment env, @Nullable TlsProperties properties,
EurekaClientHttpRequestFactorySupplier eurekaClientHttpRequestFactorySupplier,
Expand Down Expand Up @@ -109,6 +113,61 @@ public WebClientEurekaHttpClient configDiscoveryWebClientEurekaHttpClient(Eureka

}

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "org.springframework.web.client.RestClient")
@Conditional(RestClientEnabledCondition.class)
@ImportAutoConfiguration(RestClientAutoConfiguration.class)
protected static class RestClientConfiguration {

@Bean
@ConditionalOnMissingBean(EurekaHttpClient.class)
public RestClientEurekaHttpClient configDiscoveryRestClientEurekaHttpClient(EurekaClientConfigBean config,
ObjectProvider<RestClient.Builder> builder, Environment env) {
return (RestClientEurekaHttpClient) new RestClientTransportClientFactory(builder::getIfAvailable)
.newClient(HostnameBasedUrlRandomizer.randomEndpoint(config, env));
}

}

static class RestTemplateEnabledCondition extends AllNestedConditions {

RestTemplateEnabledCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}

@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", matchIfMissing = true,
havingValue = "false")
static class OnWebClientDisabled {

}

@ConditionalOnProperty(prefix = "eureka.client", name = "restclient.enabled", havingValue = "false")
static class OnRestClientDisabled {

}

}

static class RestClientEnabledCondition extends AllNestedConditions {

RestClientEnabledCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}

@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", matchIfMissing = true,
havingValue = "false")
static class OnWebClientDisabled {

}

@ConditionalOnProperty(prefix = "eureka.client", name = "restclient.enabled", matchIfMissing = true,
havingValue = "true")
static class OnRestClientDisabled {

}

}

static class EurekaConfigServerBootstrapCondition extends AllNestedConditions {

EurekaConfigServerBootstrapCondition() {
Expand Down
Loading

0 comments on commit c2c5352

Please sign in to comment.