Skip to content

Commit 47a8106

Browse files
Fix gh 629 new (#683)
* Swaps deprecated ConditionalOnEnabledEndpoint for updated annotation. Swaps with ConditionalOnAvailableEndpoint * Migrates to new OutputCaptureRule * Migrates to new OutputCaptureRule * Add health check loadBalancing implementation. * Make isAlive() method protected. Co-authored-by: Spencer Gibb <[email protected]>
1 parent 744ad89 commit 47a8106

File tree

10 files changed

+442
-31
lines changed

10 files changed

+442
-31
lines changed

docs/src/main/asciidoc/_configprops.adoc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,13 @@
2828
|spring.cloud.inetutils.timeout-seconds | 1 | Timeout, in seconds, for calculating hostname.
2929
|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.
3030
|spring.cloud.loadbalancer.cache.caffeine.spec | | The spec to use to create caches. See CaffeineSpec for more details on the spec format.
31+
|spring.cloud.loadbalancer.cache.capacity | 256 | Initial cache capacity expressed as int.
3132
|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>
33+
|spring.cloud.loadbalancer.health-check.initial-delay | 0 | Initial delay value for the HealthCheck scheduler.
34+
|spring.cloud.loadbalancer.health-check.interval | 30s | Interval for rerunning the HealthCheck scheduler.
35+
|spring.cloud.loadbalancer.health-check.path | |
3236
|spring.cloud.loadbalancer.retry.enabled | true |
3337
|spring.cloud.loadbalancer.ribbon.enabled | true | Causes `RibbonLoadBalancerClient` to be used by default.
34-
|spring.cloud.loadbalancer.zone | | A {@link String} representation of the <code>zone</code> used for filtering instances by zoned load-balancing implementations.
3538
|spring.cloud.refresh.enabled | true | Enables autoconfiguration for the refresh scope and associated features.
3639
|spring.cloud.refresh.extra-refreshable | true | Additional class names for beans to post process into refresh scope.
3740
|spring.cloud.service-registry.auto-registration.enabled | true | Whether service auto-registration is enabled. Defaults to true.

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

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -906,12 +906,71 @@ public class CustomLoadBalancerConfiguration {
906906
@Bean
907907
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
908908
ReactiveDiscoveryClient discoveryClient, Environment environment,
909-
LoadBalancerProperties loadBalancerProperties,
909+
LoadBalancerZoneConfig zoneConfig,
910910
ApplicationContext context) {
911911
DiscoveryClientServiceInstanceListSupplier firstDelegate = new DiscoveryClientServiceInstanceListSupplier(
912912
discoveryClient, environment);
913913
ZonePreferenceServiceInstanceListSupplier delegate = new ZonePreferenceServiceInstanceListSupplier(firstDelegate,
914-
loadBalancerProperties);
914+
zoneConfig);
915+
ObjectProvider<LoadBalancerCacheManager> cacheManagerProvider = context
916+
.getBeanProvider(LoadBalancerCacheManager.class);
917+
if (cacheManagerProvider.getIfAvailable() != null) {
918+
return new CachingServiceInstanceListSupplier(delegate,
919+
cacheManagerProvider.getIfAvailable());
920+
}
921+
return delegate;
922+
}
923+
}
924+
----
925+
926+
=== Instance Health-Check for LoadBalancer
927+
928+
It is possible to enable a scheduled HealthCheck for the LoadBalancer. The `HealthCheckServiceInstanceListSupplier`
929+
is provided for that. It regularly verifies if the instances provided by a delegate
930+
`ServiceInstanceListSupplier` are still alive and only returns the healthy instances,
931+
unless there are none - then it returns all the retrieved instances.
932+
933+
TIP: This mechanism is particularly helpful while using the `SimpleDiscoveryClient`. For the
934+
clients backed by an actual Service Registry, it's not necessary to use, as we already get
935+
healthy instances after querying the external ServiceDiscovery.
936+
937+
The `HealthCheckServiceInstanceListSupplier` uses `InstanceHealthChecker` to verify if the instances are
938+
alive. We provide a default `PingHealthChecker` instance. It uses `WebClient` to execute
939+
requests against the `health` endpoint of the instance. You can also provide your own implementation
940+
of `InstanceHealthChecker` instead.
941+
942+
`HealthCheckServiceInstanceListSupplier` uses properties prefixed with
943+
`spring.cloud.loadbalancer.healthcheck`. You can set the `initialDelay` and `interval`
944+
for the scheduler.
945+
946+
For the `PingHealthChecker`, you can set the default path for the healthcheck URL by setting
947+
the value of the `spring.cloud.loadbalancer.healthcheck.path.default`. You can also set a specific value
948+
for any given service by setting the value of the `spring.cloud.loadbalancer.healthcheck.path.[SERVICE_ID]`,
949+
substituting the `[SERVICE_ID]` with the correct ID of your service. If the path is not set,
950+
`/actuator/health` is used by default.
951+
952+
In order to use the health-check scheduler approach, you will have to instantiate a `HealthCheckServiceInstanceListSupplier` bean in a <<custom-loadbalancer-configuration,custom configuration>>.
953+
954+
We use delegates to work with `ServiceInstanceListSupplier` beans.
955+
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>>.
956+
957+
You could use this sample configuration to set it up:
958+
959+
[[zoned-based-custom-loadbalancer-configuration]]
960+
[source,java,indent=0]
961+
----
962+
public class CustomLoadBalancerConfiguration {
963+
964+
@Bean
965+
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
966+
ReactiveDiscoveryClient discoveryClient, Environment environment,
967+
LoadBalancerProperties loadBalancerProperties,
968+
ApplicationContext context,
969+
InstanceHealthChecker healthChecker) {
970+
DiscoveryClientServiceInstanceListSupplier firstDelegate = new DiscoveryClientServiceInstanceListSupplier(
971+
discoveryClient, environment);
972+
HealthCheckServiceInstanceListSupplier delegate = new HealthCheckServiceInstanceListSupplier(firstDelegate,
973+
loadBalancerProperties, healthChecker);
915974
ObjectProvider<LoadBalancerCacheManager> cacheManagerProvider = context
916975
.getBeanProvider(LoadBalancerCacheManager.class);
917976
if (cacheManagerProvider.getIfAvailable() != null) {

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

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@
1616

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

19+
import java.time.Duration;
20+
import java.util.Map;
21+
1922
import org.springframework.boot.context.properties.ConfigurationProperties;
23+
import org.springframework.util.LinkedCaseInsensitiveMap;
2024

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

3034
/**
31-
* A {@link String} representation of the <code>zone</code> used for filtering
32-
* instances by zoned load-balancing implementations.
35+
* Properties for <code>HealthCheckServiceInstanceListSupplier</code>.
3336
*/
34-
private String zone;
37+
private HealthCheck healthCheck = new HealthCheck();
38+
39+
public HealthCheck getHealthCheck() {
40+
return healthCheck;
41+
}
3542

36-
public String getZone() {
37-
return zone;
43+
public void setHealthCheck(HealthCheck healthCheck) {
44+
this.healthCheck = healthCheck;
3845
}
3946

40-
public void setZone(String zone) {
41-
this.zone = zone;
47+
public static class HealthCheck {
48+
49+
/**
50+
* Initial delay value for the HealthCheck scheduler.
51+
*/
52+
private int initialDelay = 0;
53+
54+
/**
55+
* Interval for rerunning the HealthCheck scheduler.
56+
*/
57+
private Duration interval = Duration.ofSeconds(30);
58+
59+
private Map<String, String> path = new LinkedCaseInsensitiveMap<>();
60+
61+
public int getInitialDelay() {
62+
return initialDelay;
63+
}
64+
65+
public void setInitialDelay(int initialDelay) {
66+
this.initialDelay = initialDelay;
67+
}
68+
69+
public Map<String, String> getPath() {
70+
return path;
71+
}
72+
73+
public void setPath(Map<String, String> path) {
74+
this.path = path;
75+
}
76+
77+
public Duration getInterval() {
78+
return interval;
79+
}
80+
81+
public void setInterval(Duration interval) {
82+
this.interval = interval;
83+
}
84+
4285
}
4386

4487
}

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

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,12 @@
1919
import org.springframework.beans.factory.ObjectProvider;
2020
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
2121
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
22-
import org.springframework.boot.context.properties.EnableConfigurationProperties;
2322
import org.springframework.cloud.client.ConditionalOnBlockingDiscoveryEnabled;
2423
import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
2524
import org.springframework.cloud.client.ConditionalOnReactiveDiscoveryEnabled;
2625
import org.springframework.cloud.client.ServiceInstance;
2726
import org.springframework.cloud.client.discovery.DiscoveryClient;
2827
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
29-
import org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerProperties;
3028
import org.springframework.cloud.loadbalancer.cache.LoadBalancerCacheManager;
3129
import org.springframework.cloud.loadbalancer.core.CachingServiceInstanceListSupplier;
3230
import org.springframework.cloud.loadbalancer.core.CachingServiceInstanceSupplier;
@@ -49,18 +47,11 @@
4947
* @author Tim Ysewyn
5048
*/
5149
@Configuration(proxyBeanMethods = false)
52-
@EnableConfigurationProperties(LoadBalancerProperties.class)
5350
@ConditionalOnDiscoveryEnabled
5451
public class LoadBalancerClientConfiguration {
5552

5653
private static final int REACTIVE_SERVICE_INSTANCE_SUPPLIER_ORDER = 193827465;
5754

58-
@Bean
59-
@ConditionalOnMissingBean
60-
LoadBalancerProperties loadBalancerProperties() {
61-
return new LoadBalancerProperties();
62-
}
63-
6455
@Bean
6556
@ConditionalOnMissingBean
6657
public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer(

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,25 @@
2222
import org.springframework.beans.factory.ObjectProvider;
2323
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
2424
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
25+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
2526
import org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerBeanPostProcessorAutoConfiguration;
27+
import org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerProperties;
2628
import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancerAutoConfiguration;
2729
import org.springframework.cloud.client.loadbalancer.reactive.ReactorLoadBalancerClientAutoConfiguration;
2830
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClientSpecification;
2931
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
3032
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
3133
import org.springframework.context.annotation.Bean;
3234
import org.springframework.context.annotation.Configuration;
35+
import org.springframework.core.env.Environment;
3336

3437
/**
3538
* @author Spencer Gibb
3639
* @author Olga Maciaszek-Sharma
3740
*/
3841
@Configuration(proxyBeanMethods = false)
3942
@LoadBalancerClients
43+
@EnableConfigurationProperties(LoadBalancerProperties.class)
4044
@AutoConfigureBefore({ ReactorLoadBalancerClientAutoConfiguration.class,
4145
LoadBalancerBeanPostProcessorAutoConfiguration.class,
4246
ReactiveLoadBalancerAutoConfiguration.class })
@@ -49,6 +53,13 @@ public LoadBalancerAutoConfiguration(
4953
this.configurations = configurations;
5054
}
5155

56+
@Bean
57+
@ConditionalOnMissingBean
58+
public LoadBalancerZoneConfig zoneConfig(Environment environment) {
59+
return new LoadBalancerZoneConfig(
60+
environment.getProperty("spring.cloud.loadbalancer.zone"));
61+
}
62+
5263
@ConditionalOnMissingBean
5364
@Bean
5465
public LoadBalancerClientFactory loadBalancerClientFactory() {
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2012-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.cloud.loadbalancer.config;
18+
19+
/**
20+
* @author Olga Maciaszek-Sharma
21+
*/
22+
public class LoadBalancerZoneConfig {
23+
24+
/**
25+
* A {@link String} representation of the <code>zone</code> used for filtering
26+
* instances by zoned load-balancing implementations.
27+
*/
28+
private String zone;
29+
30+
public LoadBalancerZoneConfig(String zone) {
31+
this.zone = zone;
32+
}
33+
34+
public String getZone() {
35+
return zone;
36+
}
37+
38+
public void setZone(String zone) {
39+
this.zone = zone;
40+
}
41+
42+
}

0 commit comments

Comments
 (0)