Browse Source

Use Ribbon by default. (#609)

* Use Ribbon by default.

* Fix after review.

* Reformat.
pull/616/head
Olga Maciaszek-Sharma 5 years ago committed by GitHub
parent
commit
87fa3c7897
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 15
      docs/src/main/asciidoc/spring-cloud-commons.adoc
  2. 4
      spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/ReactiveLoadBalancerAutoConfiguration.java
  3. 113
      spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/ReactorLoadBalancerClientAutoConfiguration.java
  4. 8
      spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/ReactiveLoadBalancerAutoConfigurationTests.java
  5. 14
      spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/config/BlockingLoadBalancerClientAutoConfiguration.java
  6. 8
      spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/config/BlockingLoadBalancerClientAutoConfigurationTests.java

15
docs/src/main/asciidoc/spring-cloud-commons.adoc

@ -407,16 +407,19 @@ public class MyClass { @@ -407,16 +407,19 @@ public class MyClass {
----
The URI needs to use a virtual host name (that is, a service name, not a host name).
The Ribbon client is used to create a full physical address.
The Ribbon client or Spring Cloud LoadBalancer is used to create a full physical address.
IMPORTANT: If you want to use a `@LoadBalanced WebClient.Builder`, you need to have a loadbalancer
implementation in the classpath. It is recommended that you add the
`org.springframework.cloud:spring-cloud-starter-loadbalancer` dependency to your project.
Then, `ReactiveLoadBalancer` will be used underneath.
Alternatively, this functionality will also work with spring-cloud-starter-netflix-ribbon, but the request
Alternatively, this functionality will also work with `spring-cloud-starter-netflix-ribbon`, but the request
will be handled by a non-reactive `LoadBalancerClient` under the hood. Additionally,
spring-cloud-starter-netflix-ribbon is already in maintenance mode, so we do not recommend
adding it to new projects.
If you have both `spring-cloud-starter-loadbalancer` and `spring-cloud-starter-netflix-ribbon`
in your classpath, Ribbon will be used by default. In order to switch to Spring Cloud LoadBalancer,
set the value of `spring.cloud.loadbalancer.ribbon.enabled` to false.
IMPORTANT: In order to make use of the more efficient cached version of `ServiceInstanceListSupplier`,
`spring-cloud-starter-loadbalancer` will *enable caching* by default.
@ -565,8 +568,14 @@ public class MyClass { @@ -565,8 +568,14 @@ public class MyClass {
----
The URI needs to use a virtual host name (that is, a service name, not a host name).
The `ReactorLoadBalancerClient` is used to create a full physical address.
The `ReactorLoadBalancer` is used to create a full physical address.
NOTE: If you have `spring-cloud-netflix-ribbon` in your classpath, <<load-balancer-exchange-filter-function,`LoadBalancerExchangeFilterFunction`>>
will be used by default to maintain backward compatibility. In order to be able to use
`ReactorLoadBalancerExchangeFilterFunction`, set the value of `spring.cloud.loadbalancer.ribbon.enabled` property
to `false`.
[[load-balancer-exchange-filter-function]]
==== Spring WebFlux WebClient with non-reactive Load Balancer Client
If you you don't have `org.springframework.cloud:spring-cloud-starter-loadbalancer` in your project,

4
spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/ReactiveLoadBalancerAutoConfiguration.java

@ -21,6 +21,7 @@ import java.util.List; @@ -21,6 +21,7 @@ import java.util.List;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@ -38,7 +39,8 @@ import org.springframework.web.reactive.function.client.WebClient; @@ -38,7 +39,8 @@ import org.springframework.web.reactive.function.client.WebClient;
@Configuration
@ConditionalOnClass(WebClient.class)
@ConditionalOnBean(LoadBalancerClient.class)
@ConditionalOnMissingBean(ReactiveLoadBalancer.Factory.class)
@AutoConfigureAfter(ReactorLoadBalancerClientAutoConfiguration.class)
@ConditionalOnMissingBean(ReactorLoadBalancerExchangeFilterFunction.class)
@Deprecated
public class ReactiveLoadBalancerAutoConfiguration {

113
spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/ReactorLoadBalancerClientAutoConfiguration.java

@ -19,12 +19,21 @@ package org.springframework.cloud.client.loadbalancer.reactive; @@ -19,12 +19,21 @@ package org.springframework.cloud.client.loadbalancer.reactive;
import java.util.Collections;
import java.util.List;
import javax.annotation.PostConstruct;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.client.WebClient;
@ -41,40 +50,92 @@ import org.springframework.web.reactive.function.client.WebClient; @@ -41,40 +50,92 @@ import org.springframework.web.reactive.function.client.WebClient;
@ConditionalOnBean(ReactiveLoadBalancer.Factory.class)
public class ReactorLoadBalancerClientAutoConfiguration {
private List<WebClient.Builder> webClientBuilders = Collections.emptyList();
List<WebClient.Builder> getBuilders() {
return this.webClientBuilders;
@Bean
@ConditionalOnClass(
name = "org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient")
public ReactorLoadBalancerClientRibbonWarnLogger reactorLoadBalancerClientRibbonWarnLogger() {
return new ReactorLoadBalancerClientRibbonWarnLogger();
}
@Bean
public SmartInitializingSingleton loadBalancedWebClientInitializer(
final List<WebClientCustomizer> customizers) {
return () -> {
for (WebClient.Builder webClientBuilder : getBuilders()) {
for (WebClientCustomizer customizer : customizers) {
customizer.customize(webClientBuilder);
@Configuration
@Conditional(OnNoRibbonDefaultCondition.class)
protected static class ReactorLoadBalancerExchangeFilterFunctionConfig {
private List<WebClient.Builder> webClientBuilders = Collections.emptyList();
List<WebClient.Builder> getBuilders() {
return this.webClientBuilders;
}
@Bean
public SmartInitializingSingleton loadBalancedWebClientInitializer(
final List<WebClientCustomizer> customizers) {
return () -> {
for (WebClient.Builder webClientBuilder : getBuilders()) {
for (WebClientCustomizer customizer : customizers) {
customizer.customize(webClientBuilder);
}
}
}
};
}
};
}
@Bean
public WebClientCustomizer loadBalancerClientWebClientCustomizer(
ReactorLoadBalancerExchangeFilterFunction filterFunction) {
return builder -> builder.filter(filterFunction);
}
@Bean
public ReactorLoadBalancerExchangeFilterFunction loadBalancerExchangeFilterFunction(
ReactiveLoadBalancer.Factory loadBalancerFactory) {
return new ReactorLoadBalancerExchangeFilterFunction(loadBalancerFactory);
}
@LoadBalanced
@Autowired(required = false)
void setWebClientBuilders(List<WebClient.Builder> webClientBuilders) {
this.webClientBuilders = webClientBuilders;
}
@Bean
public WebClientCustomizer loadBalancerClientWebClientCustomizer(
ReactorLoadBalancerExchangeFilterFunction filterFunction) {
return builder -> builder.filter(filterFunction);
}
@Bean
public ReactorLoadBalancerExchangeFilterFunction loadBalancerExchangeFilterFunction(
ReactiveLoadBalancer.Factory loadBalancerFactory) {
return new ReactorLoadBalancerExchangeFilterFunction(loadBalancerFactory);
private static final class OnNoRibbonDefaultCondition extends AnyNestedCondition {
private OnNoRibbonDefaultCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.ribbon.enabled",
havingValue = "false")
static class RibbonNotEnabled {
}
@ConditionalOnMissingClass("org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient")
static class RibbonLoadBalancerNotPresent {
}
}
@LoadBalanced
@Autowired(required = false)
void setWebClientBuilders(List<WebClient.Builder> webClientBuilders) {
this.webClientBuilders = webClientBuilders;
private static class ReactorLoadBalancerClientRibbonWarnLogger {
private static final Log LOG = LogFactory
.getLog(ReactorLoadBalancerClientRibbonWarnLogger.class);
@PostConstruct
void logWarning() {
if (LOG.isWarnEnabled()) {
LOG.warn("You have RibbonLoadBalancerClient on your classpath. "
+ "LoadBalancerExchangeFilterFunction that uses it under the "
+ "hood will be used by default. Spring Cloud Ribbon is now in maintenance mode, "
+ "so we suggest switching to "
+ ReactorLoadBalancerExchangeFilterFunction.class.getSimpleName()
+ " instead. In order to use it, set the value of `spring.cloud.loadbalancer.ribbon.enabled` to `false` or "
+ "remove spring-cloud-starter-netflix-ribbon from your project.");
}
}
}
}

8
spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/ReactiveLoadBalancerAutoConfigurationTests.java

@ -91,7 +91,7 @@ public class ReactiveLoadBalancerAutoConfigurationTests { @@ -91,7 +91,7 @@ public class ReactiveLoadBalancerAutoConfigurationTests {
}
@Test
public void autoConfigurationNotLoadedWhenReactorLoadBalancerClientPresent() {
public void autoConfigurationNotLoadedWhenReactorLoadBalancerExchangeFilterFunctionPresent() {
ConfigurableApplicationContext context = init(
ReactorLoadBalancerClientPresent.class);
final Map<String, WebClient.Builder> webClientBuilders = context
@ -139,6 +139,12 @@ public class ReactiveLoadBalancerAutoConfigurationTests { @@ -139,6 +139,12 @@ public class ReactiveLoadBalancerAutoConfigurationTests {
return serviceId -> new TestReactiveLoadBalancer();
}
@Bean
ReactorLoadBalancerExchangeFilterFunction reactorLoadBalancerExchangeFilterFunction() {
return new ReactorLoadBalancerExchangeFilterFunction(
reactiveLoadBalancerFactory());
}
}
@Configuration

14
spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/config/BlockingLoadBalancerClientAutoConfiguration.java

@ -55,8 +55,8 @@ public class BlockingLoadBalancerClientAutoConfiguration { @@ -55,8 +55,8 @@ public class BlockingLoadBalancerClientAutoConfiguration {
@Bean
@ConditionalOnClass(
name = "org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient")
public RibbonWarnLogger ribbonWarnLogger() {
return new RibbonWarnLogger();
public BlockingLoadBalancerClientRibbonWarnLogger blockingLoadBalancerClientRibbonWarnLogger() {
return new BlockingLoadBalancerClientRibbonWarnLogger();
}
@Configuration
@ -93,17 +93,19 @@ public class BlockingLoadBalancerClientAutoConfiguration { @@ -93,17 +93,19 @@ public class BlockingLoadBalancerClientAutoConfiguration {
}
static class RibbonWarnLogger {
static class BlockingLoadBalancerClientRibbonWarnLogger {
private static final Log LOG = LogFactory.getLog(RibbonWarnLogger.class);
private static final Log LOG = LogFactory
.getLog(BlockingLoadBalancerClientRibbonWarnLogger.class);
@PostConstruct
void logWarning() {
if (LOG.isWarnEnabled()) {
LOG.warn(
"You already have RibbonLoadBalancerClient on your classpath. It will be used by default. To use "
"You already have RibbonLoadBalancerClient on your classpath. It will be used by default. "
+ "As Spring Cloud Ribbon is in maintenance mode. We recommend switching to "
+ BlockingLoadBalancerClient.class.getSimpleName()
+ " set the value of `spring.cloud.loadbalancer.ribbon.enabled` to `false` or "
+ " instead. In order to use it, set the value of `spring.cloud.loadbalancer.ribbon.enabled` to `false` or "
+ "remove spring-cloud-starter-netflix-ribbon from your project.");
}
}

8
spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/config/BlockingLoadBalancerClientAutoConfigurationTests.java

@ -22,7 +22,7 @@ import org.springframework.boot.autoconfigure.AutoConfigurations; @@ -22,7 +22,7 @@ import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
import org.springframework.cloud.loadbalancer.config.BlockingLoadBalancerClientAutoConfiguration.RibbonWarnLogger;
import org.springframework.cloud.loadbalancer.config.BlockingLoadBalancerClientAutoConfiguration.BlockingLoadBalancerClientRibbonWarnLogger;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
@ -44,7 +44,8 @@ public class BlockingLoadBalancerClientAutoConfigurationTests { @@ -44,7 +44,8 @@ public class BlockingLoadBalancerClientAutoConfigurationTests {
public void beansCreatedNormally() {
applicationContextRunner.run(ctxt -> {
assertThat(ctxt).hasSingleBean(BlockingLoadBalancerClient.class);
assertThat(ctxt).doesNotHaveBean(RibbonWarnLogger.class);
assertThat(ctxt)
.doesNotHaveBean(BlockingLoadBalancerClientRibbonWarnLogger.class);
});
}
@ -54,7 +55,8 @@ public class BlockingLoadBalancerClientAutoConfigurationTests { @@ -54,7 +55,8 @@ public class BlockingLoadBalancerClientAutoConfigurationTests {
.withClassLoader(new FilteredClassLoader(RestTemplate.class))
.run(context -> {
assertThat(context).doesNotHaveBean(BlockingLoadBalancerClient.class);
assertThat(context).doesNotHaveBean(RibbonWarnLogger.class);
assertThat(context).doesNotHaveBean(
BlockingLoadBalancerClientRibbonWarnLogger.class);
});
}

Loading…
Cancel
Save