Skip to content

Commit 72e5105

Browse files
RomehRobWin
authored andcommitted
Issue 773: Aded support to rate limiter configuration customization (ReactiveX#793)
1 parent 3592deb commit 72e5105

File tree

30 files changed

+287
-118
lines changed

30 files changed

+287
-118
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package io.github.resilience4j.common;
2+
3+
import java.util.HashMap;
4+
import java.util.List;
5+
import java.util.Map;
6+
import java.util.Optional;
7+
import java.util.function.Function;
8+
import java.util.stream.Collectors;
9+
10+
/**
11+
* the composite of any spring resilience4j type config customizer implementations.
12+
*/
13+
public class CompositeCustomizer<T extends CustomizerWithName> {
14+
15+
private final Map<String, T> customizerMap = new HashMap<>();
16+
17+
public CompositeCustomizer(List<T> customizers) {
18+
if (customizers != null && !customizers.isEmpty()) {
19+
customizerMap.putAll(customizers.stream()
20+
.collect(
21+
Collectors.toMap(CustomizerWithName::name, Function.identity())));
22+
}
23+
}
24+
25+
/**
26+
* @param instanceName the resilience4j instance name
27+
* @return the found spring customizer if any .
28+
*/
29+
public Optional<T> getCustomizer(String instanceName) {
30+
return Optional.ofNullable(customizerMap.get(instanceName));
31+
}
32+
33+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package io.github.resilience4j.common;
2+
3+
/**
4+
* common interface for different spring config customizers implementation
5+
*/
6+
public interface CustomizerWithName {
7+
8+
/**
9+
* @return name of the resilience4j type instance to be customized
10+
*/
11+
String name();
12+
}
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
package io.github.resilience4j.common.circuitbreaker.configuration;
22

33
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
4+
import io.github.resilience4j.common.CustomizerWithName;
45

56
/**
67
* Enable customization circuit breaker configuration builders programmatically.
78
*/
8-
public interface CircuitBreakerConfigCustomizer {
9+
public interface CircuitBreakerConfigCustomizer extends CustomizerWithName {
910

1011
/**
1112
* Customize circuit breaker configuration builder.
@@ -14,8 +15,4 @@ public interface CircuitBreakerConfigCustomizer {
1415
*/
1516
void customize(CircuitBreakerConfig.Builder configBuilder);
1617

17-
/**
18-
* @return name of the circuit breaker instance to be customized
19-
*/
20-
String name();
2118
}

resilience4j-framework-common/src/main/java/io/github/resilience4j/common/circuitbreaker/configuration/CircuitBreakerConfigurationProperties.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig.Builder;
2121
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig.SlidingWindowType;
2222
import io.github.resilience4j.common.CommonProperties;
23+
import io.github.resilience4j.common.CompositeCustomizer;
2324
import io.github.resilience4j.common.utils.ConfigUtils;
2425
import io.github.resilience4j.core.ClassUtils;
2526
import io.github.resilience4j.core.ConfigurationNotFoundException;
@@ -52,7 +53,7 @@ public Optional<InstanceProperties> findCircuitBreakerProperties(String name) {
5253

5354
public CircuitBreakerConfig createCircuitBreakerConfig(String backendName,
5455
InstanceProperties instanceProperties,
55-
CompositeCircuitBreakerCustomizer compositeCircuitBreakerCustomizer) {
56+
CompositeCustomizer<CircuitBreakerConfigCustomizer> compositeCircuitBreakerCustomizer) {
5657
if (StringUtils.isNotEmpty(instanceProperties.getBaseConfig())) {
5758
InstanceProperties baseProperties = configs.get(instanceProperties.getBaseConfig());
5859
if (baseProperties == null) {
@@ -68,16 +69,20 @@ public CircuitBreakerConfig createCircuitBreakerConfig(String backendName,
6869

6970
private CircuitBreakerConfig buildConfigFromBaseConfig(InstanceProperties instanceProperties,
7071
InstanceProperties baseProperties,
71-
CompositeCircuitBreakerCustomizer customizerMap, String backendName) {
72+
CompositeCustomizer<CircuitBreakerConfigCustomizer> compositeCircuitBreakerCustomizer,
73+
String backendName) {
7274
ConfigUtils.mergePropertiesIfAny(instanceProperties, baseProperties);
73-
CircuitBreakerConfig baseConfig = buildConfig(custom(), baseProperties, customizerMap,
75+
CircuitBreakerConfig baseConfig = buildConfig(custom(), baseProperties,
76+
compositeCircuitBreakerCustomizer,
77+
backendName);
78+
return buildConfig(from(baseConfig), instanceProperties, compositeCircuitBreakerCustomizer,
7479
backendName);
75-
return buildConfig(from(baseConfig), instanceProperties, customizerMap, backendName);
7680
}
7781

7882
@SuppressWarnings("deprecation") // deprecated API use left for backward compatibility
7983
private CircuitBreakerConfig buildConfig(Builder builder, InstanceProperties properties,
80-
CompositeCircuitBreakerCustomizer compositeCircuitBreakerCustomizer, String backendName) {
84+
CompositeCustomizer<CircuitBreakerConfigCustomizer> compositeCircuitBreakerCustomizer,
85+
String backendName) {
8186
if (properties == null) {
8287
return builder.build();
8388
}
@@ -147,7 +152,7 @@ private CircuitBreakerConfig buildConfig(Builder builder, InstanceProperties pro
147152
builder.automaticTransitionFromOpenToHalfOpenEnabled(
148153
properties.automaticTransitionFromOpenToHalfOpenEnabled);
149154
}
150-
compositeCircuitBreakerCustomizer.getCircuitBreakerConfigCustomizer(backendName).ifPresent(
155+
compositeCircuitBreakerCustomizer.getCustomizer(backendName).ifPresent(
151156
circuitBreakerConfigCustomizer -> circuitBreakerConfigCustomizer.customize(builder));
152157
return builder.build();
153158
}

resilience4j-framework-common/src/main/java/io/github/resilience4j/common/circuitbreaker/configuration/CompositeCircuitBreakerCustomizer.java

Lines changed: 0 additions & 36 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package io.github.resilience4j.common.ratelimiter.configuration;
2+
3+
import java.util.HashMap;
4+
import java.util.List;
5+
import java.util.Map;
6+
import java.util.Optional;
7+
import java.util.function.Function;
8+
import java.util.stream.Collectors;
9+
10+
/**
11+
* the composite of any rate limiter {@link RateLimiterConfigCustomizer} implementations.
12+
*/
13+
public class CompositeRateLimiterCustomizer {
14+
15+
private final Map<String, RateLimiterConfigCustomizer> customizerMap = new HashMap<>();
16+
17+
public CompositeRateLimiterCustomizer(List<RateLimiterConfigCustomizer> customizers) {
18+
if (customizers != null && !customizers.isEmpty()) {
19+
customizerMap.putAll(customizers.stream()
20+
.collect(
21+
Collectors.toMap(RateLimiterConfigCustomizer::name, Function.identity())));
22+
}
23+
}
24+
25+
/**
26+
* @param rateLimiterInstanceName the rate limiter instance name
27+
* @return the found {@link RateLimiterConfigCustomizer} if any .
28+
*/
29+
public Optional<RateLimiterConfigCustomizer> getRateLimiterConfigCustomizer(
30+
String rateLimiterInstanceName) {
31+
return Optional.ofNullable(customizerMap.get(rateLimiterInstanceName));
32+
}
33+
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package io.github.resilience4j.common.ratelimiter.configuration;
2+
3+
import io.github.resilience4j.common.CustomizerWithName;
4+
import io.github.resilience4j.ratelimiter.RateLimiterConfig;
5+
6+
/**
7+
* Enable customization rate limiter configuration builders programmatically.
8+
*/
9+
public interface RateLimiterConfigCustomizer extends CustomizerWithName {
10+
11+
/**
12+
* Customize rate limiter configuration builder.
13+
*
14+
* @param configBuilder to be customized
15+
*/
16+
void customize(RateLimiterConfig.Builder configBuilder);
17+
18+
}

resilience4j-framework-common/src/main/java/io/github/resilience4j/common/ratelimiter/configuration/RateLimiterConfigurationProperties.java

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
import java.util.Objects;
2929
import java.util.Optional;
3030

31-
public class RateLimiterConfigurationProperties extends CommonProperties{
31+
public class RateLimiterConfigurationProperties extends CommonProperties {
3232

3333
private Map<String, InstanceProperties> instances = new HashMap<>();
3434
private Map<String, InstanceProperties> configs = new HashMap<>();
@@ -42,7 +42,8 @@ public Optional<InstanceProperties> findRateLimiterProperties(String name) {
4242
}
4343

4444
public RateLimiterConfig createRateLimiterConfig(
45-
@Nullable InstanceProperties instanceProperties) {
45+
@Nullable InstanceProperties instanceProperties,
46+
CompositeRateLimiterCustomizer compositeRateLimiterCustomizer, String instanceName) {
4647
if (instanceProperties == null) {
4748
return RateLimiterConfig.ofDefaults();
4849
}
@@ -51,21 +52,26 @@ public RateLimiterConfig createRateLimiterConfig(
5152
if (baseProperties == null) {
5253
throw new ConfigurationNotFoundException(instanceProperties.getBaseConfig());
5354
}
54-
return buildConfigFromBaseConfig(baseProperties, instanceProperties);
55+
return buildConfigFromBaseConfig(baseProperties, instanceProperties,
56+
compositeRateLimiterCustomizer, instanceName);
5557
}
56-
return buildRateLimiterConfig(RateLimiterConfig.custom(), instanceProperties);
58+
return buildRateLimiterConfig(RateLimiterConfig.custom(), instanceProperties,
59+
compositeRateLimiterCustomizer, instanceName);
5760
}
5861

5962
private RateLimiterConfig buildConfigFromBaseConfig(InstanceProperties baseProperties,
60-
InstanceProperties instanceProperties) {
63+
InstanceProperties instanceProperties,
64+
CompositeRateLimiterCustomizer compositeRateLimiterCustomizer, String instanceName) {
6165
ConfigUtils.mergePropertiesIfAny(baseProperties, instanceProperties);
6266
RateLimiterConfig baseConfig = buildRateLimiterConfig(RateLimiterConfig.custom(),
63-
baseProperties);
64-
return buildRateLimiterConfig(RateLimiterConfig.from(baseConfig), instanceProperties);
67+
baseProperties, compositeRateLimiterCustomizer, instanceName);
68+
return buildRateLimiterConfig(RateLimiterConfig.from(baseConfig), instanceProperties,
69+
compositeRateLimiterCustomizer, instanceName);
6570
}
6671

6772
private RateLimiterConfig buildRateLimiterConfig(RateLimiterConfig.Builder builder,
68-
@Nullable InstanceProperties instanceProperties) {
73+
@Nullable InstanceProperties instanceProperties,
74+
CompositeRateLimiterCustomizer compositeRateLimiterCustomizer, String instanceName) {
6975
if (instanceProperties == null) {
7076
return builder.build();
7177
}
@@ -85,16 +91,19 @@ private RateLimiterConfig buildRateLimiterConfig(RateLimiterConfig.Builder build
8591
if (instanceProperties.getWritableStackTraceEnabled() != null) {
8692
builder.writableStackTraceEnabled(instanceProperties.getWritableStackTraceEnabled());
8793
}
88-
94+
compositeRateLimiterCustomizer.getRateLimiterConfigCustomizer(instanceName).ifPresent(
95+
rateLimiterConfigCustomizer -> rateLimiterConfigCustomizer.customize(builder));
8996
return builder.build();
9097
}
9198

9299
private InstanceProperties getLimiterProperties(String limiter) {
93100
return instances.get(limiter);
94101
}
95102

96-
public RateLimiterConfig createRateLimiterConfig(String limiter) {
97-
return createRateLimiterConfig(getLimiterProperties(limiter));
103+
public RateLimiterConfig createRateLimiterConfig(String limiter,
104+
CompositeRateLimiterCustomizer compositeRateLimiterCustomizer) {
105+
return createRateLimiterConfig(getLimiterProperties(limiter),
106+
compositeRateLimiterCustomizer, limiter);
98107
}
99108

100109
@Nullable
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,17 @@
11
package io.github.resilience4j.common.retry.configuration;
22

3+
import io.github.resilience4j.common.CustomizerWithName;
34
import io.github.resilience4j.retry.RetryConfig;
45

56
/**
67
* Enable customization retry configuration builders programmatically.
78
*/
8-
public interface RetryConfigCustomizer {
9+
public interface RetryConfigCustomizer extends CustomizerWithName {
910

1011
/**
1112
* Retry configuration builder.
1213
*
1314
* @param configBuilder to be customized
1415
*/
1516
void customize(RetryConfig.Builder configBuilder);
16-
17-
/**
18-
* @return name of the retry instance to be customized
19-
*/
20-
String name();
2117
}

resilience4j-framework-common/src/test/java/io/github/resilience4j/common/circuitbreaker/configuration/CircuitBreakerConfigurationPropertiesTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package io.github.resilience4j.common.circuitbreaker.configuration;
1717

1818
import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
19+
import io.github.resilience4j.common.CompositeCustomizer;
1920
import io.github.resilience4j.common.RecordFailurePredicate;
2021
import io.github.resilience4j.core.ConfigurationNotFoundException;
2122
import org.junit.Test;
@@ -286,7 +287,7 @@ public void testIllegalArgumentOnSlowCallDurationThreshold() {
286287
defaultProperties.setSlowCallDurationThreshold(Duration.ZERO);
287288
}
288289

289-
private CompositeCircuitBreakerCustomizer compositeCircuitBreakerCustomizer() {
290-
return new CompositeCircuitBreakerCustomizer(Collections.emptyList());
290+
private CompositeCustomizer<CircuitBreakerConfigCustomizer> compositeCircuitBreakerCustomizer() {
291+
return new CompositeCustomizer<>(Collections.emptyList());
291292
}
292293
}

resilience4j-framework-common/src/test/java/io/github/resilience4j/common/ratelimiter/configuration/RateLimiterConfigurationPropertiesTest.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.junit.Test;
2121

2222
import java.time.Duration;
23+
import java.util.Collections;
2324
import java.util.HashMap;
2425
import java.util.Map;
2526

@@ -58,13 +59,13 @@ public void testRateLimiterRegistry() {
5859
assertThat(rateLimiterConfigurationProperties.getInstances().size()).isEqualTo(2);
5960
assertThat(rateLimiterConfigurationProperties.getLimiters().size()).isEqualTo(2);
6061
RateLimiterConfig rateLimiter = rateLimiterConfigurationProperties
61-
.createRateLimiterConfig("backend1");
62+
.createRateLimiterConfig("backend1", compositeRateLimiterCustomizer());
6263
assertThat(rateLimiter).isNotNull();
6364
assertThat(rateLimiter.getLimitForPeriod()).isEqualTo(2);
6465
assertThat(rateLimiter.isWritableStackTraceEnabled()).isFalse();
6566

6667
RateLimiterConfig rateLimiter2 = rateLimiterConfigurationProperties
67-
.createRateLimiterConfig("backend2");
68+
.createRateLimiterConfig("backend2", compositeRateLimiterCustomizer());
6869
assertThat(rateLimiter2).isNotNull();
6970
assertThat(rateLimiter2.getLimitForPeriod()).isEqualTo(4);
7071
assertThat(rateLimiter2.isWritableStackTraceEnabled()).isTrue();
@@ -112,23 +113,23 @@ public void testCreateRateLimiterRegistryWithSharedConfigs() {
112113

113114
// Should get default config and override LimitForPeriod
114115
RateLimiterConfig rateLimiter1 = rateLimiterConfigurationProperties
115-
.createRateLimiterConfig("backendWithDefaultConfig");
116+
.createRateLimiterConfig("backendWithDefaultConfig", compositeRateLimiterCustomizer());
116117
assertThat(rateLimiter1).isNotNull();
117118
assertThat(rateLimiter1.getLimitForPeriod()).isEqualTo(200);
118119
assertThat(rateLimiter1.getLimitRefreshPeriod()).isEqualTo(Duration.ofMillis(5));
119120
assertThat(rateLimiter1.isWritableStackTraceEnabled()).isTrue();
120121

121122
// Should get shared config and override LimitForPeriod
122123
RateLimiterConfig rateLimiter2 = rateLimiterConfigurationProperties
123-
.createRateLimiterConfig("backendWithSharedConfig");
124+
.createRateLimiterConfig("backendWithSharedConfig", compositeRateLimiterCustomizer());
124125
assertThat(rateLimiter2).isNotNull();
125126
assertThat(rateLimiter2.getLimitForPeriod()).isEqualTo(300);
126127
assertThat(rateLimiter2.getLimitRefreshPeriod()).isEqualTo(Duration.ofMillis(6));
127128
assertThat(rateLimiter2.isWritableStackTraceEnabled()).isTrue();
128129

129130
// Unknown backend should get default config of Registry
130131
RateLimiterConfig rerateLimiter3 = rateLimiterConfigurationProperties
131-
.createRateLimiterConfig("unknownBackend");
132+
.createRateLimiterConfig("unknownBackend", compositeRateLimiterCustomizer());
132133
assertThat(rerateLimiter3).isNotNull();
133134
assertThat(rerateLimiter3.getLimitForPeriod()).isEqualTo(50);
134135
assertThat(rerateLimiter3.isWritableStackTraceEnabled()).isTrue();
@@ -146,7 +147,8 @@ public void testCreateRateLimiterRegistryWithUnknownConfig() {
146147

147148
//When
148149
assertThatThrownBy(
149-
() -> rateLimiterConfigurationProperties.createRateLimiterConfig("backend"))
150+
() -> rateLimiterConfigurationProperties
151+
.createRateLimiterConfig("backend", compositeRateLimiterCustomizer()))
150152
.isInstanceOf(ConfigurationNotFoundException.class)
151153
.hasMessage("Configuration with name 'unknownConfig' does not exist");
152154
}
@@ -176,4 +178,8 @@ public void testIllegalArgumentOnEventConsumerBufferSize() {
176178
defaultProperties.setEventConsumerBufferSize(-1);
177179
}
178180

181+
private CompositeRateLimiterCustomizer compositeRateLimiterCustomizer() {
182+
return new CompositeRateLimiterCustomizer(Collections.emptyList());
183+
}
184+
179185
}

0 commit comments

Comments
 (0)