Browse Source

Merge remote-tracking branch 'origin/3.1.x' into 4.0.x

# Conflicts:
#	docs/src/main/asciidoc/spring-cloud-commons.adoc
#	spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerProperties.java
#	spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/SameInstancePreferenceServiceInstanceListSupplierTests.java
#	spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/ServiceInstanceListSupplierBuilderTests.java
pull/1278/head
Olga MaciaszekSharma 1 year ago
parent
commit
cc1aaa8883
  1. 1
      docs/src/main/asciidoc/_configprops.adoc
  2. 27
      docs/src/main/asciidoc/spring-cloud-commons.adoc
  3. 43
      spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerProperties.java
  4. 22
      spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/annotation/LoadBalancerClientConfiguration.java
  5. 4
      spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/DelegatingServiceInstanceListSupplier.java
  6. 19
      spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/SameInstancePreferenceServiceInstanceListSupplier.java
  7. 32
      spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/ServiceInstanceListSupplierBuilder.java
  8. 21
      spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/ZonePreferenceServiceInstanceListSupplier.java
  9. 8
      spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/annotation/LoadBalancerClientConfigurationTests.java
  10. 32
      spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/SameInstancePreferenceServiceInstanceListSupplierTests.java
  11. 6
      spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/ServiceInstanceListSupplierBuilderTests.java
  12. 33
      spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/ZonePreferenceServiceInstanceListSupplierTests.java

1
docs/src/main/asciidoc/_configprops.adoc

@ -37,6 +37,7 @@ @@ -37,6 +37,7 @@
|spring.cloud.loadbalancer.cache.capacity | `+++256+++` | Initial cache capacity expressed as int.
|spring.cloud.loadbalancer.cache.enabled | `+++true+++` | Enables Spring Cloud LoadBalancer caching mechanism.
|spring.cloud.loadbalancer.cache.ttl | `+++35s+++` | Time To Live - time counted from writing of the record, after which cache entries are expired, expressed as a {@link Duration}. The property {@link String} has to be in keeping with the appropriate syntax as specified in Spring Boot <code>StringToDurationConverter</code>. @see <a href= "https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/convert/StringToDurationConverter.java">StringToDurationConverter.java</a>
|spring.cloud.loadbalancer.call-get-with-request-on-delegates | `+++false+++` | If this flag is set to {@code true}, {@code ServiceInstanceListSupplier#get(Request request)} method will be implemented to call {@code delegate.get(request)} in classes assignable from {@code DelegatingServiceInstanceListSupplier} that don't already implement that method, with the exclusion of {@code CachingServiceInstanceListSupplier} and {@code HealthCheckServiceInstanceListSupplier}, which should be placed in the instance supplier hierarchy directly after the supplier performing instance retrieval over the network, before any request-based filtering is done. Note: in 4.1, this behaviour will become the default
|spring.cloud.loadbalancer.clients | |
|spring.cloud.loadbalancer.configurations | `+++default+++` | Enables a predefined LoadBalancer configuration.
|spring.cloud.loadbalancer.eager-load.clients | | Names of the clients.

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

@ -947,6 +947,12 @@ to `false`. @@ -947,6 +947,12 @@ to `false`.
WARNING: Although the basic, non-cached, implementation is useful for prototyping and testing, it's much less efficient than the cached versions, so we recommend always using the cached version in production. If the caching is already done by the `DiscoveryClient` implementation, for example `EurekaDiscoveryClient`, the load-balancer caching should be disabled to prevent double caching.
====
NOTE: When you create your own configuration, if you use `CachingServiceInstanceListSupplier` make sure to place it in the hierarchy directly after the supplier that retrieves the instances over the network, for example, `DiscoveryClientServiceInstanceListSupplier`, before any other filtering suppliers.
====
=== Weighted Load-Balancing
To enable weighted load-balancing, we provide the `WeightedServiceInstanceListSupplier`. We use `WeightFunction` to calculate the weight of each instance.
@ -1011,7 +1017,7 @@ If the zone is `null` or there are no instances within the same zone, it returns @@ -1011,7 +1017,7 @@ If the zone is `null` or there are no instances within the same zone, it returns
In order to use the zone-based load-balancing approach, you will have to instantiate a `ZonePreferenceServiceInstanceListSupplier` bean in a <<custom-loadbalancer-configuration,custom configuration>>.
We use delegates to work with `ServiceInstanceListSupplier` beans.
We suggest passing a `DiscoveryClientServiceInstanceListSupplier` delegate in the constructor of `ZonePreferenceServiceInstanceListSupplier` and, in turn, wrapping the latter with a `CachingServiceInstanceListSupplier` to leverage <<loadbalancer-caching, LoadBalancer caching mechanism>>.
We suggest using a `DiscoveryClientServiceInstanceListSupplier` delegate, wrapping it with a `CachingServiceInstanceListSupplier` to leverage <<loadbalancer-caching, LoadBalancer caching mechanism>>, and then passing the resulting bean in the constructor of `ZonePreferenceServiceInstanceListSupplier`.
You can use this sample configuration to set it up:
@ -1025,8 +1031,8 @@ public class CustomLoadBalancerConfiguration { @@ -1025,8 +1031,8 @@ public class CustomLoadBalancerConfiguration {
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withDiscoveryClient()
.withCaching()
.withZonePreference()
.withCaching()
.build(context);
}
}
@ -1089,6 +1095,12 @@ You can also pass your own `WebClient` or `RestTemplate` instance to be used for @@ -1089,6 +1095,12 @@ You can also pass your own `WebClient` or `RestTemplate` instance to be used for
WARNING: `HealthCheckServiceInstanceListSupplier` has its own caching mechanism based on Reactor Flux `replay()`. Therefore, if it's being used, you may want to skip wrapping that supplier with `CachingServiceInstanceListSupplier`.
====
NOTE: When you create your own configuration, `HealthCheckServiceInstanceListSupplier`, make sure to place it in the hierarchy directly after the supplier that retrieves the instances over the network, for example, `DiscoveryClientServiceInstanceListSupplier`, before any other filtering suppliers.
====
=== Same instance preference for LoadBalancer
You can set up the LoadBalancer in such a way that it prefers the instance that was previously selected, if that instance is available.
@ -1173,8 +1185,8 @@ public class CustomLoadBalancerConfiguration { @@ -1173,8 +1185,8 @@ public class CustomLoadBalancerConfiguration {
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withDiscoveryClient()
.withCaching()
.withHints()
.withCaching()
.build(context);
}
}
@ -1284,11 +1296,18 @@ public class MyConfiguration { @@ -1284,11 +1296,18 @@ public class MyConfiguration {
}
}
----
====
NOTE: The classes you pass as `@LoadBalancerClient` or `@LoadBalancerClients` configuration arguments should either not be annotated with `@Configuration` or be outside component scan scope.
====
====
NOTE: When you create your own configuration, if you use `CachingServiceInstanceListSupplier` or `HealthCheckServiceInstanceListSupplier`, makes sure to use one of them, not both, and make sure to place it in the hierarchy directly after the supplier that retrieves the instances over the network, for example, `DiscoveryClientServiceInstanceListSupplier`, before any other filtering suppliers.
====
[[loadbalancer-lifecycle]]
=== Spring Cloud LoadBalancer Lifecycle
@ -1356,6 +1375,8 @@ The per-client configuration properties work for most of the properties, apart f @@ -1356,6 +1375,8 @@ The per-client configuration properties work for most of the properties, apart f
NOTE: For the properties where maps where already used, where you can specify a different value per-client without using the `clients` keyword (for example, `hints`, `health-check.path`), we have kept that behaviour in order to keep the library backwards compatible. It will be modified in the next major release.
NOTE: Starting with `4.0.4`, we have introduced the `callGetWithRequestOnDelegates` flag in `LoadBalancerProperties`. If this flag is set to `true`, `ServiceInstanceListSupplier#get(Request request)` method will be implemented to call `delegate.get(request)` in classes assignable from `DelegatingServiceInstanceListSupplier` that don't already implement that method, with the exclusion of `CachingServiceInstanceListSupplier` and `HealthCheckServiceInstanceListSupplier`, which should be placed in the instance supplier hierarchy directly after the supplier performing instance retrieval over the network, before any request-based filtering is done. For `4.0.x` the flag is set to `false` by default, however, since `4.1.0` it's going to be set to `true` by default.
=== AOT and Native Image Support
Since `4.0.0`, Spring Cloud LoadBalancer supports Spring AOT transformations and native images. However, to use this feature, you need to explicitly define your `LoadBalancerClient` service IDs. You can do so by using the `value` or `name` attributes of the `@LoadBalancerClient` annotation or as values of the `spring.cloud.loadbalancer.eager-load.clients` property.

43
spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerProperties.java

@ -72,6 +72,19 @@ public class LoadBalancerProperties { @@ -72,6 +72,19 @@ public class LoadBalancerProperties {
*/
private StickySession stickySession = new StickySession();
/**
* If this flag is set to {@code true},
* {@code ServiceInstanceListSupplier#get(Request request)} method will be implemented
* to call {@code delegate.get(request)} in classes assignable from
* {@code DelegatingServiceInstanceListSupplier} that don't already implement that
* method, with the exclusion of {@code CachingServiceInstanceListSupplier} and
* {@code HealthCheckServiceInstanceListSupplier}, which should be placed in the
* instance supplier hierarchy directly after the supplier performing instance
* retrieval over the network, before any request-based filtering is done. Note: in
* 4.1, this behaviour will become the default
*/
private boolean callGetWithRequestOnDelegates;
public HealthCheck getHealthCheck() {
return healthCheck;
}
@ -125,6 +138,36 @@ public class LoadBalancerProperties { @@ -125,6 +138,36 @@ public class LoadBalancerProperties {
return xForwarded;
}
/**
* If this flag is set to {@code true},
* {@code ServiceInstanceListSupplier#get(Request request)} method will be implemented
* to call {@code delegate.get(request)} in classes assignable from
* {@code DelegatingServiceInstanceListSupplier} that don't already implement that
* method, with the exclusion of {@code CachingServiceInstanceListSupplier} and
* {@code HealthCheckServiceInstanceListSupplier}, which should be placed in the
* instance supplier hierarchy directly after the supplier performing instance
* retrieval over the network, before any request-based filtering is done. Note: in
* 4.1, this behaviour will become the default
*/
public boolean isCallGetWithRequestOnDelegates() {
return callGetWithRequestOnDelegates;
}
/**
* If this flag is set to {@code true},
* {@code ServiceInstanceListSupplier#get(Request request)} method will be implemented
* to call {@code delegate.get(request)} in classes assignable from
* {@code DelegatingServiceInstanceListSupplier} that don't already implement that
* method, with the exclusion of {@code CachingServiceInstanceListSupplier} and
* {@code HealthCheckServiceInstanceListSupplier}, which should be placed in the
* instance supplier hierarchy directly after the supplier performing instance
* retrieval over the network, before any request-based filtering is done. Note: in
* 4.1, this behaviour will become the default
*/
public void setCallGetWithRequestOnDelegates(boolean callGetWithRequestOnDelegates) {
this.callGetWithRequestOnDelegates = callGetWithRequestOnDelegates;
}
public static class StickySession {
/**

22
spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/annotation/LoadBalancerClientConfiguration.java

@ -94,7 +94,7 @@ public class LoadBalancerClientConfiguration { @@ -94,7 +94,7 @@ public class LoadBalancerClientConfiguration {
@Conditional(ZonePreferenceConfigurationCondition.class)
public ServiceInstanceListSupplier zonePreferenceDiscoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder().withDiscoveryClient().withZonePreference().withCaching()
return ServiceInstanceListSupplier.builder().withDiscoveryClient().withCaching().withZonePreference()
.build(context);
}
@ -120,8 +120,8 @@ public class LoadBalancerClientConfiguration { @@ -120,8 +120,8 @@ public class LoadBalancerClientConfiguration {
@Conditional(RequestBasedStickySessionConfigurationCondition.class)
public ServiceInstanceListSupplier requestBasedStickySessionDiscoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder().withDiscoveryClient().withRequestBasedStickySession()
.withCaching().build(context);
return ServiceInstanceListSupplier.builder().withDiscoveryClient().withCaching()
.withRequestBasedStickySession().build(context);
}
@Bean
@ -130,8 +130,8 @@ public class LoadBalancerClientConfiguration { @@ -130,8 +130,8 @@ public class LoadBalancerClientConfiguration {
@Conditional(SameInstancePreferenceConfigurationCondition.class)
public ServiceInstanceListSupplier sameInstancePreferenceServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder().withDiscoveryClient().withSameInstancePreference()
.withCaching().build(context);
return ServiceInstanceListSupplier.builder().withDiscoveryClient().withCaching()
.withSameInstancePreference().build(context);
}
@Bean
@ -165,8 +165,8 @@ public class LoadBalancerClientConfiguration { @@ -165,8 +165,8 @@ public class LoadBalancerClientConfiguration {
@Conditional(ZonePreferenceConfigurationCondition.class)
public ServiceInstanceListSupplier zonePreferenceDiscoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient().withZonePreference()
.withCaching().build(context);
return ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient().withCaching()
.withZonePreference().build(context);
}
@Bean
@ -185,8 +185,8 @@ public class LoadBalancerClientConfiguration { @@ -185,8 +185,8 @@ public class LoadBalancerClientConfiguration {
@Conditional(RequestBasedStickySessionConfigurationCondition.class)
public ServiceInstanceListSupplier requestBasedStickySessionDiscoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient().withRequestBasedStickySession()
.withCaching().build(context);
return ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient().withCaching()
.withRequestBasedStickySession().build(context);
}
@Bean
@ -195,8 +195,8 @@ public class LoadBalancerClientConfiguration { @@ -195,8 +195,8 @@ public class LoadBalancerClientConfiguration {
@Conditional(SameInstancePreferenceConfigurationCondition.class)
public ServiceInstanceListSupplier sameInstancePreferenceServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient().withSameInstancePreference()
.withCaching().build(context);
return ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient().withCaching()
.withSameInstancePreference().build(context);
}
@Bean

4
spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/DelegatingServiceInstanceListSupplier.java

@ -40,12 +40,12 @@ public abstract class DelegatingServiceInstanceListSupplier @@ -40,12 +40,12 @@ public abstract class DelegatingServiceInstanceListSupplier
}
public ServiceInstanceListSupplier getDelegate() {
return this.delegate;
return delegate;
}
@Override
public String getServiceId() {
return this.delegate.getServiceId();
return delegate.getServiceId();
}
@Override

19
spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/SameInstancePreferenceServiceInstanceListSupplier.java

@ -24,6 +24,8 @@ import org.apache.commons.logging.LogFactory; @@ -24,6 +24,8 @@ import org.apache.commons.logging.LogFactory;
import reactor.core.publisher.Flux;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer;
/**
* An implementation of {@link ServiceInstanceListSupplier} that selects the previously
@ -40,10 +42,19 @@ public class SameInstancePreferenceServiceInstanceListSupplier extends Delegatin @@ -40,10 +42,19 @@ public class SameInstancePreferenceServiceInstanceListSupplier extends Delegatin
private ServiceInstance previouslyReturnedInstance;
private boolean callGetWithRequestOnDelegates;
public SameInstancePreferenceServiceInstanceListSupplier(ServiceInstanceListSupplier delegate) {
super(delegate);
}
public SameInstancePreferenceServiceInstanceListSupplier(ServiceInstanceListSupplier delegate,
ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerClientFactory) {
super(delegate);
callGetWithRequestOnDelegates = loadBalancerClientFactory.getProperties(getServiceId())
.isCallGetWithRequestOnDelegates();
}
@Override
public String getServiceId() {
return delegate.getServiceId();
@ -54,6 +65,14 @@ public class SameInstancePreferenceServiceInstanceListSupplier extends Delegatin @@ -54,6 +65,14 @@ public class SameInstancePreferenceServiceInstanceListSupplier extends Delegatin
return delegate.get().map(this::filteredBySameInstancePreference);
}
@Override
public Flux<List<ServiceInstance>> get(Request request) {
if (callGetWithRequestOnDelegates) {
return delegate.get(request).map(this::filteredBySameInstancePreference);
}
return get();
}
private List<ServiceInstance> filteredBySameInstancePreference(List<ServiceInstance> serviceInstances) {
if (previouslyReturnedInstance != null && serviceInstances.contains(previouslyReturnedInstance)) {
if (LOG.isDebugEnabled()) {

32
spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/ServiceInstanceListSupplierBuilder.java

@ -58,8 +58,6 @@ public final class ServiceInstanceListSupplierBuilder { @@ -58,8 +58,6 @@ public final class ServiceInstanceListSupplierBuilder {
private Creator baseCreator;
private DelegateCreator cachingCreator;
private final List<DelegateCreator> creators = new ArrayList<>();
ServiceInstanceListSupplierBuilder() {
@ -174,8 +172,10 @@ public final class ServiceInstanceListSupplierBuilder { @@ -174,8 +172,10 @@ public final class ServiceInstanceListSupplierBuilder {
* @return the {@link ServiceInstanceListSupplierBuilder} object
*/
public ServiceInstanceListSupplierBuilder withSameInstancePreference() {
DelegateCreator creator = (context,
delegate) -> new SameInstancePreferenceServiceInstanceListSupplier(delegate);
DelegateCreator creator = (context, delegate) -> {
LoadBalancerClientFactory loadBalancerClientFactory = context.getBean(LoadBalancerClientFactory.class);
return new SameInstancePreferenceServiceInstanceListSupplier(delegate, loadBalancerClientFactory);
};
this.creators.add(creator);
return this;
}
@ -217,8 +217,9 @@ public final class ServiceInstanceListSupplierBuilder { @@ -217,8 +217,9 @@ public final class ServiceInstanceListSupplierBuilder {
*/
public ServiceInstanceListSupplierBuilder withZonePreference() {
DelegateCreator creator = (context, delegate) -> {
LoadBalancerClientFactory loadBalancerClientFactory = context.getBean(LoadBalancerClientFactory.class);
LoadBalancerZoneConfig zoneConfig = context.getBean(LoadBalancerZoneConfig.class);
return new ZonePreferenceServiceInstanceListSupplier(delegate, zoneConfig);
return new ZonePreferenceServiceInstanceListSupplier(delegate, zoneConfig, loadBalancerClientFactory);
};
this.creators.add(creator);
return this;
@ -232,8 +233,9 @@ public final class ServiceInstanceListSupplierBuilder { @@ -232,8 +233,9 @@ public final class ServiceInstanceListSupplierBuilder {
*/
public ServiceInstanceListSupplierBuilder withZonePreference(String zoneName) {
DelegateCreator creator = (context, delegate) -> {
LoadBalancerClientFactory loadBalancerClientFactory = context.getBean(LoadBalancerClientFactory.class);
LoadBalancerZoneConfig zoneConfig = new LoadBalancerZoneConfig(zoneName);
return new ZonePreferenceServiceInstanceListSupplier(delegate, zoneConfig);
return new ZonePreferenceServiceInstanceListSupplier(delegate, zoneConfig, loadBalancerClientFactory);
};
this.creators.add(creator);
return this;
@ -254,19 +256,15 @@ public final class ServiceInstanceListSupplierBuilder { @@ -254,19 +256,15 @@ public final class ServiceInstanceListSupplierBuilder {
}
/**
* If {@link LoadBalancerCacheManager} is available in the context, wraps created
* {@link ServiceInstanceListSupplier} hierarchy with a
* {@link CachingServiceInstanceListSupplier} instance to provide a caching mechanism
* for service instances. Uses {@link ObjectProvider} to lazily resolve
* If {@link LoadBalancerCacheManager} is available in the context, adds a
* {@link CachingServiceInstanceListSupplier} instance to the
* {@link ServiceInstanceListSupplier} hierarchy to provide a caching mechanism for
* service instances. Uses {@link ObjectProvider} to lazily resolve
* {@link LoadBalancerCacheManager}.
* @return the {@link ServiceInstanceListSupplierBuilder} object
*/
public ServiceInstanceListSupplierBuilder withCaching() {
if (cachingCreator != null && LOG.isWarnEnabled()) {
LOG.warn(
"Overriding a previously set cachingCreator with a CachingServiceInstanceListSupplier-based cachingCreator.");
}
this.cachingCreator = (context, delegate) -> {
DelegateCreator creator = (context, delegate) -> {
ObjectProvider<LoadBalancerCacheManager> cacheManagerProvider = context
.getBeanProvider(LoadBalancerCacheManager.class);
if (cacheManagerProvider.getIfAvailable() != null) {
@ -277,6 +275,7 @@ public final class ServiceInstanceListSupplierBuilder { @@ -277,6 +275,7 @@ public final class ServiceInstanceListSupplierBuilder {
}
return delegate;
};
creators.add(creator);
return this;
}
@ -323,9 +322,6 @@ public final class ServiceInstanceListSupplierBuilder { @@ -323,9 +322,6 @@ public final class ServiceInstanceListSupplierBuilder {
supplier = creator.apply(context, supplier);
}
if (this.cachingCreator != null) {
supplier = this.cachingCreator.apply(context, supplier);
}
return supplier;
}

21
spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/ZonePreferenceServiceInstanceListSupplier.java

@ -23,6 +23,8 @@ import java.util.Map; @@ -23,6 +23,8 @@ import java.util.Map;
import reactor.core.publisher.Flux;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancer;
import org.springframework.cloud.loadbalancer.config.LoadBalancerZoneConfig;
/**
@ -43,17 +45,36 @@ public class ZonePreferenceServiceInstanceListSupplier extends DelegatingService @@ -43,17 +45,36 @@ public class ZonePreferenceServiceInstanceListSupplier extends DelegatingService
private String zone;
private boolean callGetWithRequestOnDelegates;
public ZonePreferenceServiceInstanceListSupplier(ServiceInstanceListSupplier delegate,
LoadBalancerZoneConfig zoneConfig) {
super(delegate);
this.zoneConfig = zoneConfig;
}
public ZonePreferenceServiceInstanceListSupplier(ServiceInstanceListSupplier delegate,
LoadBalancerZoneConfig zoneConfig,
ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerClientFactory) {
super(delegate);
this.zoneConfig = zoneConfig;
callGetWithRequestOnDelegates = loadBalancerClientFactory.getProperties(getServiceId())
.isCallGetWithRequestOnDelegates();
}
@Override
public Flux<List<ServiceInstance>> get() {
return getDelegate().get().map(this::filteredByZone);
}
@Override
public Flux<List<ServiceInstance>> get(Request request) {
if (callGetWithRequestOnDelegates) {
return getDelegate().get(request).map(this::filteredByZone);
}
return get();
}
private List<ServiceInstance> filteredByZone(List<ServiceInstance> serviceInstances) {
if (zone == null) {
zone = zoneConfig.getZone();

8
spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/annotation/LoadBalancerClientConfigurationTests.java

@ -93,10 +93,10 @@ class LoadBalancerClientConfigurationTests { @@ -93,10 +93,10 @@ class LoadBalancerClientConfigurationTests {
reactiveDiscoveryClientRunner.withPropertyValues("spring.cloud.loadbalancer.configurations=zone-preference")
.run(context -> {
ServiceInstanceListSupplier supplier = context.getBean(ServiceInstanceListSupplier.class);
then(supplier).isInstanceOf(CachingServiceInstanceListSupplier.class);
then(supplier).isInstanceOf(ZonePreferenceServiceInstanceListSupplier.class);
ServiceInstanceListSupplier delegate = ((DelegatingServiceInstanceListSupplier) supplier)
.getDelegate();
then(delegate).isInstanceOf(ZonePreferenceServiceInstanceListSupplier.class);
then(delegate).isInstanceOf(CachingServiceInstanceListSupplier.class);
ServiceInstanceListSupplier secondDelegate = ((DelegatingServiceInstanceListSupplier) delegate)
.getDelegate();
then(secondDelegate).isInstanceOf(DiscoveryClientServiceInstanceListSupplier.class);
@ -136,10 +136,10 @@ class LoadBalancerClientConfigurationTests { @@ -136,10 +136,10 @@ class LoadBalancerClientConfigurationTests {
.withPropertyValues("spring.cloud.loadbalancer.configurations=request-based-sticky-session")
.run(context -> {
ServiceInstanceListSupplier supplier = context.getBean(ServiceInstanceListSupplier.class);
then(supplier).isInstanceOf(CachingServiceInstanceListSupplier.class);
then(supplier).isInstanceOf(RequestBasedStickySessionServiceInstanceListSupplier.class);
ServiceInstanceListSupplier delegate = ((DelegatingServiceInstanceListSupplier) supplier)
.getDelegate();
then(delegate).isInstanceOf(RequestBasedStickySessionServiceInstanceListSupplier.class);
then(delegate).isInstanceOf(CachingServiceInstanceListSupplier.class);
ServiceInstanceListSupplier secondDelegate = ((DelegatingServiceInstanceListSupplier) delegate)
.getDelegate();
then(secondDelegate).isInstanceOf(DiscoveryClientServiceInstanceListSupplier.class);

32
spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/SameInstancePreferenceServiceInstanceListSupplierTests.java

@ -19,11 +19,17 @@ package org.springframework.cloud.loadbalancer.core; @@ -19,11 +19,17 @@ package org.springframework.cloud.loadbalancer.core;
import java.util.Arrays;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Flux;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultRequest;
import org.springframework.cloud.client.loadbalancer.DefaultRequestContext;
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
@ -43,8 +49,9 @@ class SameInstancePreferenceServiceInstanceListSupplierTests { @@ -43,8 +49,9 @@ class SameInstancePreferenceServiceInstanceListSupplierTests {
private final DiscoveryClientServiceInstanceListSupplier delegate = mock(
DiscoveryClientServiceInstanceListSupplier.class);
private final SameInstancePreferenceServiceInstanceListSupplier supplier = new SameInstancePreferenceServiceInstanceListSupplier(
delegate);
private final LoadBalancerClientFactory loadBalancerClientFactory = mock(LoadBalancerClientFactory.class);
private SameInstancePreferenceServiceInstanceListSupplier supplier;
private final ServiceInstance first = serviceInstance("test-1");
@ -52,6 +59,14 @@ class SameInstancePreferenceServiceInstanceListSupplierTests { @@ -52,6 +59,14 @@ class SameInstancePreferenceServiceInstanceListSupplierTests {
private final ServiceInstance third = serviceInstance("test-3");
@BeforeEach
void setUp() {
LoadBalancerProperties properties = new LoadBalancerProperties();
properties.setCallGetWithRequestOnDelegates(true);
when(loadBalancerClientFactory.getProperties(any())).thenReturn(properties);
supplier = new SameInstancePreferenceServiceInstanceListSupplier(delegate, loadBalancerClientFactory);
}
@Test
void shouldReturnPreviouslySelectedInstanceIfAvailable() {
when(delegate.get()).thenReturn(Flux.just(Arrays.asList(first, second, third)));
@ -73,7 +88,7 @@ class SameInstancePreferenceServiceInstanceListSupplierTests { @@ -73,7 +88,7 @@ class SameInstancePreferenceServiceInstanceListSupplierTests {
}
@Test
void shouldReturnAllInstancesFromDelegateIfPreviouslySelectedInstanceIfAvailable() {
void shouldReturnAllInstancesFromDelegateIfPreviouslySelectedInstanceIsNotAvailable() {
when(delegate.get()).thenReturn(Flux.just(Arrays.asList(second, third)));
supplier.selectedServiceInstance(first);
@ -82,6 +97,17 @@ class SameInstancePreferenceServiceInstanceListSupplierTests { @@ -82,6 +97,17 @@ class SameInstancePreferenceServiceInstanceListSupplierTests {
assertThat(instances).hasSize(2);
}
@Test
void shouldCallGetRequestOnDelegate() {
Request<DefaultRequestContext> request = new DefaultRequest<>(new DefaultRequestContext());
when(delegate.get()).thenReturn(Flux.just(Arrays.asList(first, second, third)));
when(delegate.get(request)).thenReturn(Flux.just(Arrays.asList(first, second)));
List<ServiceInstance> instances = supplier.get(request).blockFirst();
assertThat(instances).hasSize(2);
}
@Test
void shouldCallSelectedServiceInstanceOnItsDelegate() {
ServiceInstance firstInstance = serviceInstance("test-4");

6
spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/ServiceInstanceListSupplierBuilderTests.java

@ -37,11 +37,9 @@ public class ServiceInstanceListSupplierBuilderTests { @@ -37,11 +37,9 @@ public class ServiceInstanceListSupplierBuilderTests {
public void testBuilder() {
new ApplicationContextRunner().withUserConfiguration(CacheTestConfig.class).run(context -> {
ServiceInstanceListSupplier supplier = ServiceInstanceListSupplier.builder().withDiscoveryClient()
.withHealthChecks().withWeighted().withCaching().build(context);
assertThat(supplier).isInstanceOf(CachingServiceInstanceListSupplier.class);
.withHealthChecks().withWeighted().build(context);
assertThat(supplier).isInstanceOf(WeightedServiceInstanceListSupplier.class);
DelegatingServiceInstanceListSupplier delegating = (DelegatingServiceInstanceListSupplier) supplier;
assertThat(delegating.getDelegate()).isInstanceOf(WeightedServiceInstanceListSupplier.class);
delegating = (DelegatingServiceInstanceListSupplier) delegating.getDelegate();
assertThat(delegating.getDelegate()).isInstanceOf(HealthCheckServiceInstanceListSupplier.class);
delegating = (DelegatingServiceInstanceListSupplier) delegating.getDelegate();
assertThat(delegating.getDelegate()).isInstanceOf(DiscoveryClientServiceInstanceListSupplier.class);

33
spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/ZonePreferenceServiceInstanceListSupplierTests.java

@ -22,15 +22,22 @@ import java.util.HashMap; @@ -22,15 +22,22 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Flux;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultRequest;
import org.springframework.cloud.client.loadbalancer.DefaultRequestContext;
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.loadbalancer.config.LoadBalancerZoneConfig;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatCode;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -46,8 +53,9 @@ class ZonePreferenceServiceInstanceListSupplierTests { @@ -46,8 +53,9 @@ class ZonePreferenceServiceInstanceListSupplierTests {
private final LoadBalancerZoneConfig zoneConfig = new LoadBalancerZoneConfig(null);
private final ZonePreferenceServiceInstanceListSupplier supplier = new ZonePreferenceServiceInstanceListSupplier(
delegate, zoneConfig);
private ZonePreferenceServiceInstanceListSupplier supplier;
private final LoadBalancerClientFactory loadBalancerClientFactory = mock(LoadBalancerClientFactory.class);
private final ServiceInstance first = serviceInstance("test-1", buildZoneMetadata("zone1"));
@ -59,6 +67,14 @@ class ZonePreferenceServiceInstanceListSupplierTests { @@ -59,6 +67,14 @@ class ZonePreferenceServiceInstanceListSupplierTests {
private final ServiceInstance fifth = serviceInstance("test-5", buildZoneMetadata(null));
@BeforeEach
void setUp() {
LoadBalancerProperties properties = new LoadBalancerProperties();
properties.setCallGetWithRequestOnDelegates(true);
when(loadBalancerClientFactory.getProperties(any())).thenReturn(properties);
supplier = new ZonePreferenceServiceInstanceListSupplier(delegate, zoneConfig, loadBalancerClientFactory);
}
@Test
void shouldFilterInstancesByZone() {
zoneConfig.setZone("zone1");
@ -73,6 +89,19 @@ class ZonePreferenceServiceInstanceListSupplierTests { @@ -73,6 +89,19 @@ class ZonePreferenceServiceInstanceListSupplierTests {
assertThat(filtered).doesNotContain(fifth);
}
@Test
void shouldCallGetRequestOnDelegate() {
zoneConfig.setZone("zone1");
Request<DefaultRequestContext> request = new DefaultRequest<>(new DefaultRequestContext());
when(delegate.get()).thenReturn(Flux.just(Arrays.asList(first, second, third, fourth, fifth)));
when(delegate.get(request)).thenReturn(Flux.just(Arrays.asList(first, third, fourth, fifth)));
List<ServiceInstance> filtered = supplier.get(request).blockFirst();
assertThat(filtered).hasSize(1);
assertThat(filtered).containsOnly(first);
}
@Test
void shouldReturnAllInstancesIfNoZoneInstances() {
zoneConfig.setZone("zone1");

Loading…
Cancel
Save