Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion docs/src/main/asciidoc/_configprops.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,13 @@
|spring.cloud.inetutils.timeout-seconds | 1 | Timeout, in seconds, for calculating hostname.
|spring.cloud.inetutils.use-only-site-local-interfaces | false | Whether to use only interfaces with site local addresses. See {@link InetAddress#isSiteLocalAddress()} for more details.
|spring.cloud.loadbalancer.cache.caffeine.spec | | The spec to use to create caches. See CaffeineSpec for more details on the spec format.
|spring.cloud.loadbalancer.cache.capacity | 256 | Initial cache capacity expressed as int.
|spring.cloud.loadbalancer.cache.ttl | 30s | 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.health-check.initial-delay | 0 | Initial delay value for the HealthCheck scheduler.
|spring.cloud.loadbalancer.health-check.interval | 30s | Interval for rerunning the HealthCheck scheduler.
|spring.cloud.loadbalancer.health-check.path | |
|spring.cloud.loadbalancer.retry.enabled | true |
|spring.cloud.loadbalancer.ribbon.enabled | true | Causes `RibbonLoadBalancerClient` to be used by default.
|spring.cloud.loadbalancer.zone | | A {@link String} representation of the <code>zone</code> used for filtering instances by zoned load-balancing implementations.
|spring.cloud.refresh.enabled | true | Enables autoconfiguration for the refresh scope and associated features.
|spring.cloud.refresh.extra-refreshable | true | Additional class names for beans to post process into refresh scope.
|spring.cloud.service-registry.auto-registration.enabled | true | Whether service auto-registration is enabled. Defaults to true.
Expand Down
63 changes: 61 additions & 2 deletions docs/src/main/asciidoc/spring-cloud-commons.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -906,12 +906,71 @@ public class CustomLoadBalancerConfiguration {
@Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ReactiveDiscoveryClient discoveryClient, Environment environment,
LoadBalancerProperties loadBalancerProperties,
LoadBalancerZoneConfig zoneConfig,
ApplicationContext context) {
DiscoveryClientServiceInstanceListSupplier firstDelegate = new DiscoveryClientServiceInstanceListSupplier(
discoveryClient, environment);
ZonePreferenceServiceInstanceListSupplier delegate = new ZonePreferenceServiceInstanceListSupplier(firstDelegate,
loadBalancerProperties);
zoneConfig);
ObjectProvider<LoadBalancerCacheManager> cacheManagerProvider = context
.getBeanProvider(LoadBalancerCacheManager.class);
if (cacheManagerProvider.getIfAvailable() != null) {
return new CachingServiceInstanceListSupplier(delegate,
cacheManagerProvider.getIfAvailable());
}
return delegate;
}
}
----

=== Instance Health-Check for LoadBalancer

It is possible to enable a scheduled HealthCheck for the LoadBalancer. The `HealthCheckServiceInstanceListSupplier`
is provided for that. It regularly verifies if the instances provided by a delegate
`ServiceInstanceListSupplier` are still alive and only returns the healthy instances,
unless there are none - then it returns all the retrieved instances.

TIP: This mechanism is particularly helpful while using the `SimpleDiscoveryClient`. For the
clients backed by an actual Service Registry, it's not necessary to use, as we already get
healthy instances after querying the external ServiceDiscovery.

The `HealthCheckServiceInstanceListSupplier` uses `InstanceHealthChecker` to verify if the instances are
alive. We provide a default `PingHealthChecker` instance. It uses `WebClient` to execute
requests against the `health` endpoint of the instance. You can also provide your own implementation
of `InstanceHealthChecker` instead.

`HealthCheckServiceInstanceListSupplier` uses properties prefixed with
`spring.cloud.loadbalancer.healthcheck`. You can set the `initialDelay` and `interval`
for the scheduler.

For the `PingHealthChecker`, you can set the default path for the healthcheck URL by setting
the value of the `spring.cloud.loadbalancer.healthcheck.path.default`. You can also set a specific value
for any given service by setting the value of the `spring.cloud.loadbalancer.healthcheck.path.[SERVICE_ID]`,
substituting the `[SERVICE_ID]` with the correct ID of your service. If the path is not set,
`/actuator/health` is used by default.

In order to use the health-check scheduler approach, you will have to instantiate a `HealthCheckServiceInstanceListSupplier` 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 `HealthCheckServiceInstanceListSupplier` and, in turn, wrapping the latter with a `CachingServiceInstanceListSupplier` to leverage <<loadbalancer-caching, LoadBalancer caching mechanism>>.

You could use this sample configuration to set it up:

[[zoned-based-custom-loadbalancer-configuration]]
[source,java,indent=0]
----
public class CustomLoadBalancerConfiguration {

@Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ReactiveDiscoveryClient discoveryClient, Environment environment,
LoadBalancerProperties loadBalancerProperties,
ApplicationContext context,
InstanceHealthChecker healthChecker) {
DiscoveryClientServiceInstanceListSupplier firstDelegate = new DiscoveryClientServiceInstanceListSupplier(
discoveryClient, environment);
HealthCheckServiceInstanceListSupplier delegate = new HealthCheckServiceInstanceListSupplier(firstDelegate,
loadBalancerProperties, healthChecker);
ObjectProvider<LoadBalancerCacheManager> cacheManagerProvider = context
.getBeanProvider(LoadBalancerCacheManager.class);
if (cacheManagerProvider.getIfAvailable() != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@

package org.springframework.cloud.client.loadbalancer.reactive;

import java.time.Duration;
import java.util.Map;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.util.LinkedCaseInsensitiveMap;

/**
* A {@link ConfigurationProperties} bean for Spring Cloud LoadBalancer.
Expand All @@ -28,17 +32,56 @@
public class LoadBalancerProperties {

/**
* A {@link String} representation of the <code>zone</code> used for filtering
* instances by zoned load-balancing implementations.
* Properties for <code>HealthCheckServiceInstanceListSupplier</code>.
*/
private String zone;
private HealthCheck healthCheck = new HealthCheck();

public HealthCheck getHealthCheck() {
return healthCheck;
}

public String getZone() {
return zone;
public void setHealthCheck(HealthCheck healthCheck) {
this.healthCheck = healthCheck;
}

public void setZone(String zone) {
this.zone = zone;
public static class HealthCheck {

/**
* Initial delay value for the HealthCheck scheduler.
*/
private int initialDelay = 0;

/**
* Interval for rerunning the HealthCheck scheduler.
*/
private Duration interval = Duration.ofSeconds(30);

private Map<String, String> path = new LinkedCaseInsensitiveMap<>();

public int getInitialDelay() {
return initialDelay;
}

public void setInitialDelay(int initialDelay) {
this.initialDelay = initialDelay;
}

public Map<String, String> getPath() {
return path;
}

public void setPath(Map<String, String> path) {
this.path = path;
}

public Duration getInterval() {
return interval;
}

public void setInterval(Duration interval) {
this.interval = interval;
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,12 @@
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.ConditionalOnBlockingDiscoveryEnabled;
import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
import org.springframework.cloud.client.ConditionalOnReactiveDiscoveryEnabled;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerProperties;
import org.springframework.cloud.loadbalancer.cache.LoadBalancerCacheManager;
import org.springframework.cloud.loadbalancer.core.CachingServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.core.CachingServiceInstanceSupplier;
Expand All @@ -49,18 +47,11 @@
* @author Tim Ysewyn
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(LoadBalancerProperties.class)
@ConditionalOnDiscoveryEnabled
public class LoadBalancerClientConfiguration {

private static final int REACTIVE_SERVICE_INSTANCE_SUPPLIER_ORDER = 193827465;

@Bean
@ConditionalOnMissingBean
LoadBalancerProperties loadBalancerProperties() {
return new LoadBalancerProperties();
}

@Bean
@ConditionalOnMissingBean
public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,21 +22,25 @@
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerBeanPostProcessorAutoConfiguration;
import org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerProperties;
import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancerAutoConfiguration;
import org.springframework.cloud.client.loadbalancer.reactive.ReactorLoadBalancerClientAutoConfiguration;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClientSpecification;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

/**
* @author Spencer Gibb
* @author Olga Maciaszek-Sharma
*/
@Configuration(proxyBeanMethods = false)
@LoadBalancerClients
@EnableConfigurationProperties(LoadBalancerProperties.class)
@AutoConfigureBefore({ ReactorLoadBalancerClientAutoConfiguration.class,
LoadBalancerBeanPostProcessorAutoConfiguration.class,
ReactiveLoadBalancerAutoConfiguration.class })
Expand All @@ -49,6 +53,13 @@ public LoadBalancerAutoConfiguration(
this.configurations = configurations;
}

@Bean
@ConditionalOnMissingBean
public LoadBalancerZoneConfig zoneConfig(Environment environment) {
return new LoadBalancerZoneConfig(
environment.getProperty("spring.cloud.loadbalancer.zone"));
}

@ConditionalOnMissingBean
@Bean
public LoadBalancerClientFactory loadBalancerClientFactory() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright 2012-2019 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.loadbalancer.config;

/**
* @author Olga Maciaszek-Sharma
*/
public class LoadBalancerZoneConfig {

/**
* A {@link String} representation of the <code>zone</code> used for filtering
* instances by zoned load-balancing implementations.
*/
private String zone;

public LoadBalancerZoneConfig(String zone) {
this.zone = zone;
}

public String getZone() {
return zone;
}

public void setZone(String zone) {
this.zone = zone;
}

}
Loading