Skip to content

Commit f30afc1

Browse files
Handle conditionals for client properties (#1033)
1 parent 68d43a1 commit f30afc1

File tree

8 files changed

+299
-27
lines changed

8 files changed

+299
-27
lines changed

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1269,6 +1269,15 @@ spring:
12691269

12701270
The above example will result in a merged health-check `@ConfigurationProperties` object with `initial-delay=1s` and `interval=30s`.
12711271

1272+
The per-client configuration properties work for most of the properties, apart from the following global ones:
1273+
1274+
- `spring.cloud.loadbalancer.enabled` - globally enables or disables load-balancing
1275+
- `spring.cloud.loadbalancer.retry.enabled` - globally enables or disables load-balanced retries. If you enable it globally, you can still disable retries for specific clients using the `client`-prefixed properties, but not the other way round
1276+
- `spring.cloud.loadbalancer.cache.enabled` - globally enables or disables LoadBalancer caching. If you enable it globally, you can still disable caching for specific clients by creating a <<custom-loadbalancer-configuration, custom configuration>> that does not include the `CachingServiceInstanceListSupplier` in the `ServiceInstanceListSupplier` delegates hierarchy, but not the other way round.
1277+
- `spring.cloud.loadbalancer.stats.micrometer.enabled` - globally enables or disables LoadBalancer Micrometer metrics
1278+
1279+
NOTE: For the properties where maps where already used, where you could 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.
1280+
12721281
== Spring Cloud Circuit Breaker
12731282

12741283
include::spring-cloud-circuitbreaker.adoc[leveloffset=+1]

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

Lines changed: 78 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,24 @@
3030
import org.springframework.cloud.client.ServiceInstance;
3131
import org.springframework.cloud.client.discovery.DiscoveryClient;
3232
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
33+
import org.springframework.cloud.loadbalancer.config.XForwardedConfigurationCondition;
3334
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
3435
import org.springframework.cloud.loadbalancer.core.RetryAwareServiceInstanceListSupplier;
3536
import org.springframework.cloud.loadbalancer.core.RoundRobinLoadBalancer;
3637
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
3738
import org.springframework.cloud.loadbalancer.core.XForwardedHeadersTransformer;
3839
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
40+
import org.springframework.cloud.loadbalancer.support.LoadBalancerEnvironmentPropertyUtils;
3941
import org.springframework.context.ConfigurableApplicationContext;
4042
import org.springframework.context.annotation.Bean;
43+
import org.springframework.context.annotation.Condition;
44+
import org.springframework.context.annotation.ConditionContext;
4145
import org.springframework.context.annotation.Conditional;
4246
import org.springframework.context.annotation.Configuration;
4347
import org.springframework.context.annotation.Primary;
4448
import org.springframework.core.annotation.Order;
4549
import org.springframework.core.env.Environment;
50+
import org.springframework.core.type.AnnotatedTypeMetadata;
4651
import org.springframework.retry.support.RetryTemplate;
4752
import org.springframework.web.client.RestTemplate;
4853
import org.springframework.web.reactive.function.client.WebClient;
@@ -76,8 +81,7 @@ public static class ReactiveSupportConfiguration {
7681
@Bean
7782
@ConditionalOnBean(ReactiveDiscoveryClient.class)
7883
@ConditionalOnMissingBean
79-
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "default",
80-
matchIfMissing = true)
84+
@Conditional(DefaultConfigurationCondition.class)
8185
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
8286
ConfigurableApplicationContext context) {
8387
return ServiceInstanceListSupplier.builder().withDiscoveryClient().withCaching().build(context);
@@ -86,7 +90,7 @@ public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
8690
@Bean
8791
@ConditionalOnBean(ReactiveDiscoveryClient.class)
8892
@ConditionalOnMissingBean
89-
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "zone-preference")
93+
@Conditional(ZonePreferenceConfigurationCondition.class)
9094
public ServiceInstanceListSupplier zonePreferenceDiscoveryClientServiceInstanceListSupplier(
9195
ConfigurableApplicationContext context) {
9296
return ServiceInstanceListSupplier.builder().withDiscoveryClient().withZonePreference().withCaching()
@@ -96,15 +100,15 @@ public ServiceInstanceListSupplier zonePreferenceDiscoveryClientServiceInstanceL
96100
@Bean
97101
@ConditionalOnBean({ XForwardedHeadersTransformer.class, LoadBalancerClientFactory.class })
98102
@ConditionalOnMissingBean
99-
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.xForwarded.enabledXforwarded", havingValue = "true")
103+
@Conditional(XForwardedConfigurationCondition.class)
100104
public XForwardedHeadersTransformer xForwarderHeadersTransformer(LoadBalancerClientFactory clientFactory) {
101105
return new XForwardedHeadersTransformer(clientFactory);
102106
}
103107

104108
@Bean
105109
@ConditionalOnBean({ ReactiveDiscoveryClient.class, WebClient.Builder.class })
106110
@ConditionalOnMissingBean
107-
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "health-check")
111+
@Conditional(HealthCheckConfigurationCondition.class)
108112
public ServiceInstanceListSupplier healthCheckDiscoveryClientServiceInstanceListSupplier(
109113
ConfigurableApplicationContext context) {
110114
return ServiceInstanceListSupplier.builder().withDiscoveryClient().withHealthChecks().build(context);
@@ -113,8 +117,7 @@ public ServiceInstanceListSupplier healthCheckDiscoveryClientServiceInstanceList
113117
@Bean
114118
@ConditionalOnBean(ReactiveDiscoveryClient.class)
115119
@ConditionalOnMissingBean
116-
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations",
117-
havingValue = "request-based-sticky-session")
120+
@Conditional(RequestBasedStickySessionConfigurationCondition.class)
118121
public ServiceInstanceListSupplier requestBasedStickySessionDiscoveryClientServiceInstanceListSupplier(
119122
ConfigurableApplicationContext context) {
120123
return ServiceInstanceListSupplier.builder().withDiscoveryClient().withRequestBasedStickySession()
@@ -124,8 +127,7 @@ public ServiceInstanceListSupplier requestBasedStickySessionDiscoveryClientServi
124127
@Bean
125128
@ConditionalOnBean(ReactiveDiscoveryClient.class)
126129
@ConditionalOnMissingBean
127-
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations",
128-
havingValue = "same-instance-preference")
130+
@Conditional(SameInstancePreferenceConfigurationCondition.class)
129131
public ServiceInstanceListSupplier sameInstancePreferenceServiceInstanceListSupplier(
130132
ConfigurableApplicationContext context) {
131133
return ServiceInstanceListSupplier.builder().withDiscoveryClient().withSameInstancePreference()
@@ -142,8 +144,7 @@ public static class BlockingSupportConfiguration {
142144
@Bean
143145
@ConditionalOnBean(DiscoveryClient.class)
144146
@ConditionalOnMissingBean
145-
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "default",
146-
matchIfMissing = true)
147+
@Conditional(DefaultConfigurationCondition.class)
147148
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
148149
ConfigurableApplicationContext context) {
149150
return ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient().withCaching().build(context);
@@ -152,7 +153,7 @@ public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
152153
@Bean
153154
@ConditionalOnBean(DiscoveryClient.class)
154155
@ConditionalOnMissingBean
155-
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "zone-preference")
156+
@Conditional(ZonePreferenceConfigurationCondition.class)
156157
public ServiceInstanceListSupplier zonePreferenceDiscoveryClientServiceInstanceListSupplier(
157158
ConfigurableApplicationContext context) {
158159
return ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient().withZonePreference()
@@ -162,7 +163,7 @@ public ServiceInstanceListSupplier zonePreferenceDiscoveryClientServiceInstanceL
162163
@Bean
163164
@ConditionalOnBean({ DiscoveryClient.class, RestTemplate.class })
164165
@ConditionalOnMissingBean
165-
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations", havingValue = "health-check")
166+
@Conditional(HealthCheckConfigurationCondition.class)
166167
public ServiceInstanceListSupplier healthCheckDiscoveryClientServiceInstanceListSupplier(
167168
ConfigurableApplicationContext context) {
168169
return ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient().withBlockingHealthChecks()
@@ -172,8 +173,7 @@ public ServiceInstanceListSupplier healthCheckDiscoveryClientServiceInstanceList
172173
@Bean
173174
@ConditionalOnBean(DiscoveryClient.class)
174175
@ConditionalOnMissingBean
175-
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations",
176-
havingValue = "request-based-sticky-session")
176+
@Conditional(RequestBasedStickySessionConfigurationCondition.class)
177177
public ServiceInstanceListSupplier requestBasedStickySessionDiscoveryClientServiceInstanceListSupplier(
178178
ConfigurableApplicationContext context) {
179179
return ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient().withRequestBasedStickySession()
@@ -183,8 +183,7 @@ public ServiceInstanceListSupplier requestBasedStickySessionDiscoveryClientServi
183183
@Bean
184184
@ConditionalOnBean(DiscoveryClient.class)
185185
@ConditionalOnMissingBean
186-
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.configurations",
187-
havingValue = "same-instance-preference")
186+
@Conditional(SameInstancePreferenceConfigurationCondition.class)
188187
public ServiceInstanceListSupplier sameInstancePreferenceServiceInstanceListSupplier(
189188
ConfigurableApplicationContext context) {
190189
return ServiceInstanceListSupplier.builder().withBlockingDiscoveryClient().withSameInstancePreference()
@@ -241,8 +240,7 @@ static class LoadBalancerRetryEnabled {
241240

242241
}
243242

244-
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.avoid-previous-instance", havingValue = "true",
245-
matchIfMissing = true)
243+
@Conditional(AvoidPreviousInstanceEnabledCondition.class)
246244
static class AvoidPreviousInstanceEnabled {
247245

248246
}
@@ -260,12 +258,71 @@ static class LoadBalancerRetryEnabled {
260258

261259
}
262260

263-
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.avoid-previous-instance", havingValue = "true",
264-
matchIfMissing = true)
261+
@Conditional(AvoidPreviousInstanceEnabledCondition.class)
265262
static class AvoidPreviousInstanceEnabled {
266263

267264
}
268265

269266
}
270267

268+
static class AvoidPreviousInstanceEnabledCondition implements Condition {
269+
270+
@Override
271+
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
272+
return LoadBalancerEnvironmentPropertyUtils.trueOrMissingForClientOrDefault(context.getEnvironment(),
273+
"retry.avoid-previous-instance");
274+
}
275+
276+
}
277+
278+
static class DefaultConfigurationCondition implements Condition {
279+
280+
@Override
281+
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
282+
return LoadBalancerEnvironmentPropertyUtils.equalToOrMissingForClientOrDefault(context.getEnvironment(),
283+
"configurations", "default");
284+
}
285+
286+
}
287+
288+
static class ZonePreferenceConfigurationCondition implements Condition {
289+
290+
@Override
291+
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
292+
return LoadBalancerEnvironmentPropertyUtils.equalToForClientOrDefault(context.getEnvironment(),
293+
"configurations", "zone-preference");
294+
}
295+
296+
}
297+
298+
static class HealthCheckConfigurationCondition implements Condition {
299+
300+
@Override
301+
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
302+
return LoadBalancerEnvironmentPropertyUtils.equalToForClientOrDefault(context.getEnvironment(),
303+
"configurations", "health-check");
304+
}
305+
306+
}
307+
308+
static class RequestBasedStickySessionConfigurationCondition implements Condition {
309+
310+
@Override
311+
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
312+
return LoadBalancerEnvironmentPropertyUtils.equalToForClientOrDefault(context.getEnvironment(),
313+
"configurations", "request-based-sticky-session");
314+
}
315+
316+
}
317+
318+
static class SameInstancePreferenceConfigurationCondition implements Condition {
319+
320+
@Override
321+
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
322+
return LoadBalancerEnvironmentPropertyUtils.equalToForClientOrDefault(context.getEnvironment(),
323+
"configurations", "same-instance-preference");
324+
}
325+
326+
}
327+
271328
}

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

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
2222
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2323
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
24-
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
2524
import org.springframework.boot.context.properties.EnableConfigurationProperties;
2625
import org.springframework.cloud.client.ServiceInstance;
2726
import org.springframework.cloud.client.loadbalancer.AsyncLoadBalancerAutoConfiguration;
@@ -36,8 +35,13 @@
3635
import org.springframework.cloud.loadbalancer.blocking.retry.BlockingLoadBalancedRetryFactory;
3736
import org.springframework.cloud.loadbalancer.core.LoadBalancerServiceInstanceCookieTransformer;
3837
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
38+
import org.springframework.cloud.loadbalancer.support.LoadBalancerEnvironmentPropertyUtils;
3939
import org.springframework.context.annotation.Bean;
40+
import org.springframework.context.annotation.Condition;
41+
import org.springframework.context.annotation.ConditionContext;
42+
import org.springframework.context.annotation.Conditional;
4043
import org.springframework.context.annotation.Configuration;
44+
import org.springframework.core.type.AnnotatedTypeMetadata;
4145
import org.springframework.retry.support.RetryTemplate;
4246
import org.springframework.web.client.RestTemplate;
4347

@@ -64,16 +68,15 @@ public LoadBalancerClient blockingLoadBalancerClient(LoadBalancerClientFactory l
6468
}
6569

6670
@Bean
67-
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.sticky-session.add-service-instance-cookie",
68-
havingValue = "true")
71+
@Conditional(AddServiceInstanceCookieCondition.class)
6972
@ConditionalOnMissingBean(LoadBalancerServiceInstanceCookieTransformer.class)
7073
public LoadBalancerServiceInstanceCookieTransformer loadBalancerServiceInstanceCookieTransformer(
7174
LoadBalancerProperties properties) {
7275
return new LoadBalancerServiceInstanceCookieTransformer(properties.getStickySession());
7376
}
7477

7578
@Bean
76-
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.xforwarded.enabledXforwarded", havingValue = "true")
79+
@Conditional(XForwardedConfigurationCondition.class)
7780
@ConditionalOnMissingBean(XForwardedHeadersTransformer.class)
7881
@ConditionalOnBean(LoadBalancerClientFactory.class)
7982
public XForwardedHeadersTransformer xForwarderHeadersTransformer(
@@ -95,4 +98,14 @@ LoadBalancedRetryFactory loadBalancedRetryFactory(
9598

9699
}
97100

101+
static class AddServiceInstanceCookieCondition implements Condition {
102+
103+
@Override
104+
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
105+
return LoadBalancerEnvironmentPropertyUtils.trueForClientOrDefault(context.getEnvironment(),
106+
"sticky-session.add-service-instance-cookie");
107+
}
108+
109+
}
110+
98111
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright 2012-2021 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+
import org.springframework.cloud.loadbalancer.support.LoadBalancerEnvironmentPropertyUtils;
20+
import org.springframework.context.annotation.Condition;
21+
import org.springframework.context.annotation.ConditionContext;
22+
import org.springframework.core.type.AnnotatedTypeMetadata;
23+
24+
/**
25+
* Condition matched when x-forwarded headers are enabled via properties.
26+
*
27+
* @author Olga Maciaszek-Sharma
28+
* @since 3.1.0
29+
*/
30+
public class XForwardedConfigurationCondition implements Condition {
31+
32+
@Override
33+
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
34+
return LoadBalancerEnvironmentPropertyUtils.trueForClientOrDefault(context.getEnvironment(),
35+
"spring.cloud.loadbalancer.x-forwarded.enabled");
36+
}
37+
38+
}

spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/support/LoadBalancerClientFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public LoadBalancerClientFactory(LoadBalancerClientsProperties properties) {
6969
this.properties = properties;
7070
}
7171

72-
public String getName(Environment environment) {
72+
public static String getName(Environment environment) {
7373
return environment.getProperty(PROPERTY_NAME);
7474
}
7575

0 commit comments

Comments
 (0)