Skip to content

Commit

Permalink
Issue 773': Added Circuit breaker Config Customizer Support (Reactive…
Browse files Browse the repository at this point in the history
  • Loading branch information
Romeh authored and RobWin committed Jan 7, 2020
1 parent 3e1ce36 commit 7c5c724
Show file tree
Hide file tree
Showing 16 changed files with 240 additions and 62 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.github.resilience4j.common.circuitbreaker.configuration;

import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;

/**
* Enable customization circuit breaker configuration builders programmatically.
*/
public interface CircuitBreakerConfigCustomizer {

/**
* Customize circuit breaker configuration builder.
*
* @param configBuilder to be customized
*/
void customize(CircuitBreakerConfig.Builder configBuilder);

/**
* @return name of the circuit breaker instance to be customized
*/
String name();
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,26 +50,34 @@ public Optional<InstanceProperties> findCircuitBreakerProperties(String name) {
return Optional.ofNullable(instanceProperties);
}

public CircuitBreakerConfig createCircuitBreakerConfig(InstanceProperties instanceProperties) {
public CircuitBreakerConfig createCircuitBreakerConfig(String backendName,
InstanceProperties instanceProperties,
CompositeCircuitBreakerCustomizer compositeCircuitBreakerCustomizer) {
if (StringUtils.isNotEmpty(instanceProperties.getBaseConfig())) {
InstanceProperties baseProperties = configs.get(instanceProperties.getBaseConfig());
if (baseProperties == null) {
throw new ConfigurationNotFoundException(instanceProperties.getBaseConfig());
}
return buildConfigFromBaseConfig(instanceProperties, baseProperties);
return buildConfigFromBaseConfig(instanceProperties, baseProperties,
compositeCircuitBreakerCustomizer,
backendName);
}
return buildConfig(custom(), instanceProperties);
return buildConfig(custom(), instanceProperties, compositeCircuitBreakerCustomizer,
backendName);
}

private CircuitBreakerConfig buildConfigFromBaseConfig(InstanceProperties instanceProperties,
InstanceProperties baseProperties) {
InstanceProperties baseProperties,
CompositeCircuitBreakerCustomizer customizerMap, String backendName) {
ConfigUtils.mergePropertiesIfAny(instanceProperties, baseProperties);
CircuitBreakerConfig baseConfig = buildConfig(custom(), baseProperties);
return buildConfig(from(baseConfig), instanceProperties);
CircuitBreakerConfig baseConfig = buildConfig(custom(), baseProperties, customizerMap,
backendName);
return buildConfig(from(baseConfig), instanceProperties, customizerMap, backendName);
}

@SuppressWarnings("deprecation") // deprecated API use left for backward compatibility
private CircuitBreakerConfig buildConfig(Builder builder, InstanceProperties properties) {
private CircuitBreakerConfig buildConfig(Builder builder, InstanceProperties properties,
CompositeCircuitBreakerCustomizer compositeCircuitBreakerCustomizer, String backendName) {
if (properties == null) {
return builder.build();
}
Expand Down Expand Up @@ -139,7 +147,8 @@ private CircuitBreakerConfig buildConfig(Builder builder, InstanceProperties pro
builder.automaticTransitionFromOpenToHalfOpenEnabled(
properties.automaticTransitionFromOpenToHalfOpenEnabled);
}

compositeCircuitBreakerCustomizer.getCircuitBreakerConfigCustomizer(backendName).ifPresent(
circuitBreakerConfigCustomizer -> circuitBreakerConfigCustomizer.customize(builder));
return builder.build();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package io.github.resilience4j.common.circuitbreaker.configuration;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
* the composite of any circuit breaker {@link CircuitBreakerConfigCustomizer} implementations.
*/
public class CompositeCircuitBreakerCustomizer {

final Map<String, CircuitBreakerConfigCustomizer> customizerMap = new HashMap<>();

public CompositeCircuitBreakerCustomizer(List<CircuitBreakerConfigCustomizer> customizers) {

if (customizers != null && !customizers.isEmpty()) {
customizerMap.putAll(customizers.stream()
.collect(
Collectors.toMap(CircuitBreakerConfigCustomizer::name, Function.identity())));
}

}

/**
* @param circuitBreakerInstanceName the circuit breaker instance name
* @return the found {@link CircuitBreakerConfigCustomizer} if any .
*/
public Optional<CircuitBreakerConfigCustomizer> getCircuitBreakerConfigCustomizer(
String circuitBreakerInstanceName) {
return Optional.ofNullable(customizerMap.get(circuitBreakerInstanceName));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.junit.Test;

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

Expand Down Expand Up @@ -69,7 +70,8 @@ public void testCreateCircuitBreakerRegistry() {
assertThat(circuitBreakerConfigurationProperties.getInstances().size()).isEqualTo(2);

CircuitBreakerConfig circuitBreaker1 = circuitBreakerConfigurationProperties
.createCircuitBreakerConfig(instanceProperties1);
.createCircuitBreakerConfig("backend1", instanceProperties1,
compositeCircuitBreakerCustomizer());
assertThat(circuitBreaker1).isNotNull();
assertThat(circuitBreaker1.getSlidingWindowSize()).isEqualTo(200);
assertThat(circuitBreaker1.getSlidingWindowType())
Expand All @@ -87,10 +89,15 @@ public void testCreateCircuitBreakerRegistry() {
.getBackendProperties("backend1");
assertThat(circuitBreakerConfigurationProperties.findCircuitBreakerProperties("backend1"))
.isNotEmpty();
assertThat(circuitBreakerConfigurationProperties.findCircuitBreakerProperties("backend1").get().getRegisterHealthIndicator()).isTrue();
assertThat(circuitBreakerConfigurationProperties.findCircuitBreakerProperties("backend1").get().getAllowHealthIndicatorToFail()).isTrue();
assertThat(
circuitBreakerConfigurationProperties.findCircuitBreakerProperties("backend1").get()
.getRegisterHealthIndicator()).isTrue();
assertThat(
circuitBreakerConfigurationProperties.findCircuitBreakerProperties("backend1").get()
.getAllowHealthIndicatorToFail()).isTrue();
CircuitBreakerConfig circuitBreaker2 = circuitBreakerConfigurationProperties
.createCircuitBreakerConfig(instanceProperties2);
.createCircuitBreakerConfig("backend2", instanceProperties2,
compositeCircuitBreakerCustomizer());
assertThat(circuitBreaker2).isNotNull();
assertThat(circuitBreaker2.getSlidingWindowSize()).isEqualTo(1337);

Expand All @@ -113,17 +120,19 @@ public void testCircuitBreakerIntervalFunctionProperties() {
CircuitBreakerConfigurationProperties circuitBreakerConfigurationProperties = new CircuitBreakerConfigurationProperties();
circuitBreakerConfigurationProperties.getInstances().put("backend1", instanceProperties1);
circuitBreakerConfigurationProperties.getInstances().put("backend2", instanceProperties2);
Map<String,String> globalTagsForCircuitBreakers=new HashMap<>();
globalTagsForCircuitBreakers.put("testKey1","testKet2");
Map<String, String> globalTagsForCircuitBreakers = new HashMap<>();
globalTagsForCircuitBreakers.put("testKey1", "testKet2");
circuitBreakerConfigurationProperties.setTags(globalTagsForCircuitBreakers);
//Then
assertThat(circuitBreakerConfigurationProperties.getInstances().size()).isEqualTo(2);
assertThat(circuitBreakerConfigurationProperties.getTags()).isNotEmpty();
assertThat(circuitBreakerConfigurationProperties.getBackends().size()).isEqualTo(2);
final CircuitBreakerConfig circuitBreakerConfig1 = circuitBreakerConfigurationProperties
.createCircuitBreakerConfig(instanceProperties1);
.createCircuitBreakerConfig("backend1", instanceProperties1,
compositeCircuitBreakerCustomizer());
final CircuitBreakerConfig circuitBreakerConfig2 = circuitBreakerConfigurationProperties
.createCircuitBreakerConfig(instanceProperties2);
.createCircuitBreakerConfig("backend2", instanceProperties2,
compositeCircuitBreakerCustomizer());
CircuitBreakerConfigurationProperties.InstanceProperties instancePropertiesForRetry1 = circuitBreakerConfigurationProperties
.getInstances().get("backend1");
assertThat(instancePropertiesForRetry1.getWaitDurationInOpenState().toMillis())
Expand Down Expand Up @@ -171,14 +180,16 @@ public void testCreateCircuitBreakerRegistryWithSharedConfigs() {

// Should get default config and overwrite setRingBufferSizeInHalfOpenState
CircuitBreakerConfig circuitBreaker1 = circuitBreakerConfigurationProperties
.createCircuitBreakerConfig(backendWithDefaultConfig);
.createCircuitBreakerConfig("backendWithDefaultConfig", backendWithDefaultConfig,
compositeCircuitBreakerCustomizer());
assertThat(circuitBreaker1).isNotNull();
assertThat(circuitBreaker1.getSlidingWindowSize()).isEqualTo(1000);
assertThat(circuitBreaker1.getPermittedNumberOfCallsInHalfOpenState()).isEqualTo(99);

// Should get shared config and overwrite setRingBufferSizeInHalfOpenState
CircuitBreakerConfig circuitBreaker2 = circuitBreakerConfigurationProperties
.createCircuitBreakerConfig(backendWithSharedConfig);
.createCircuitBreakerConfig("backendWithSharedConfig", backendWithSharedConfig,
compositeCircuitBreakerCustomizer());
assertThat(circuitBreaker2).isNotNull();
assertThat(circuitBreaker2.getSlidingWindowSize()).isEqualTo(1337);
assertThat(circuitBreaker2.getSlidingWindowType())
Expand All @@ -188,7 +199,8 @@ public void testCreateCircuitBreakerRegistryWithSharedConfigs() {
// Unknown backend should get default config of Registry
CircuitBreakerConfig circuitBreaker3 = circuitBreakerConfigurationProperties
.createCircuitBreakerConfig(
new CircuitBreakerConfigurationProperties.InstanceProperties());
"UN_KNOWN", new CircuitBreakerConfigurationProperties.InstanceProperties(),
compositeCircuitBreakerCustomizer());
assertThat(circuitBreaker3).isNotNull();
assertThat(circuitBreaker3.getSlidingWindowSize())
.isEqualTo(CircuitBreakerConfig.DEFAULT_SLIDING_WINDOW_SIZE);
Expand All @@ -205,7 +217,8 @@ public void testCreateCircuitBreakerRegistryWithUnknownConfig() {

//When
assertThatThrownBy(() -> circuitBreakerConfigurationProperties
.createCircuitBreakerConfig(instanceProperties))
.createCircuitBreakerConfig("backend", instanceProperties,
compositeCircuitBreakerCustomizer()))
.isInstanceOf(ConfigurationNotFoundException.class)
.hasMessage("Configuration with name 'unknownConfig' does not exist");
}
Expand Down Expand Up @@ -272,4 +285,8 @@ public void testIllegalArgumentOnSlowCallDurationThreshold() {
CircuitBreakerConfigurationProperties.InstanceProperties defaultProperties = new CircuitBreakerConfigurationProperties.InstanceProperties();
defaultProperties.setSlowCallDurationThreshold(Duration.ZERO);
}

private CompositeCircuitBreakerCustomizer compositeCircuitBreakerCustomizer() {
return new CompositeCircuitBreakerCustomizer(Collections.emptyList());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import io.github.resilience4j.common.bulkhead.configuration.BulkheadConfigurationProperties;
import io.github.resilience4j.common.bulkhead.configuration.ThreadPoolBulkheadConfigurationProperties;
import io.github.resilience4j.common.circuitbreaker.configuration.CircuitBreakerConfigurationProperties;
import io.github.resilience4j.common.circuitbreaker.configuration.CompositeCircuitBreakerCustomizer;
import io.github.resilience4j.common.ratelimiter.configuration.RateLimiterConfigurationProperties;
import io.github.resilience4j.common.retry.configuration.RetryConfigurationProperties;
import io.github.resilience4j.consumer.DefaultEventConsumerRegistry;
Expand Down Expand Up @@ -67,6 +68,7 @@

import javax.inject.Inject;
import javax.inject.Provider;
import java.util.Collections;
import java.util.Map;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -206,15 +208,18 @@ public CircuitBreakerRegistry get() {
Map<String, CircuitBreakerConfig> configs = circuitBreakerProperties.getConfigs()
.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,
entry -> circuitBreakerProperties
.createCircuitBreakerConfig(entry.getValue())));
.createCircuitBreakerConfig(entry.getKey(), entry.getValue(),
new CompositeCircuitBreakerCustomizer(Collections.emptyList()))));
CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.of(configs);

// build circuit breakers
EndpointsConfig endpointsConfig = resilience4jConfig.getEndpoints();
circuitBreakerProperties.getInstances().forEach((name, circuitBreakerConfig) -> {
io.github.resilience4j.circuitbreaker.CircuitBreaker circuitBreaker =
circuitBreakerRegistry.circuitBreaker(name,
circuitBreakerProperties.createCircuitBreakerConfig(circuitBreakerConfig));
circuitBreakerProperties.createCircuitBreakerConfig(name,
circuitBreakerConfig,
new CompositeCircuitBreakerCustomizer(Collections.emptyList())));
if (endpointsConfig.getCircuitbreaker().isEnabled()) {
circuitBreaker.getEventPublisher().onEvent(eventConsumerRegistry
.createEventConsumer(name,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
import io.github.resilience4j.circuitbreaker.configure.*;
import io.github.resilience4j.circuitbreaker.event.CircuitBreakerEvent;
import io.github.resilience4j.common.circuitbreaker.configuration.CompositeCircuitBreakerCustomizer;
import io.github.resilience4j.consumer.EventConsumerRegistry;
import io.github.resilience4j.core.registry.RegistryEventConsumer;
import io.github.resilience4j.fallback.FallbackDecorators;
Expand Down Expand Up @@ -51,17 +52,11 @@ public AbstractCircuitBreakerConfigurationOnMissingBean(
@ConditionalOnMissingBean
public CircuitBreakerRegistry circuitBreakerRegistry(
EventConsumerRegistry<CircuitBreakerEvent> eventConsumerRegistry,
RegistryEventConsumer<CircuitBreaker> circuitBreakerRegistryEventConsumer) {
CircuitBreakerRegistry circuitBreakerRegistry =
circuitBreakerConfiguration.createCircuitBreakerRegistry(circuitBreakerProperties,
circuitBreakerRegistryEventConsumer);
// Register the event consumers
circuitBreakerConfiguration
.registerEventConsumer(circuitBreakerRegistry, eventConsumerRegistry);
// Initialize backends that were initially configured.
circuitBreakerConfiguration.initCircuitBreakerRegistry(circuitBreakerRegistry);

return circuitBreakerRegistry;
RegistryEventConsumer<CircuitBreaker> circuitBreakerRegistryEventConsumer,
CompositeCircuitBreakerCustomizer compositeCircuitBreakerCustomizer) {
return circuitBreakerConfiguration
.circuitBreakerRegistry(eventConsumerRegistry, circuitBreakerRegistryEventConsumer,
compositeCircuitBreakerCustomizer);
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
import io.github.resilience4j.circuitbreaker.autoconfigure.AbstractCircuitBreakerConfigurationOnMissingBean;
import io.github.resilience4j.circuitbreaker.configure.CircuitBreakerConfigurationProperties;
import io.github.resilience4j.common.bulkhead.configuration.ThreadPoolBulkheadConfigurationProperties;
import io.github.resilience4j.common.circuitbreaker.configuration.CircuitBreakerConfigCustomizer;
import io.github.resilience4j.common.circuitbreaker.configuration.CompositeCircuitBreakerCustomizer;
import io.github.resilience4j.consumer.DefaultEventConsumerRegistry;
import io.github.resilience4j.core.registry.CompositeRegistryEventConsumer;
import io.github.resilience4j.fallback.CompletionStageFallbackDecorator;
Expand Down Expand Up @@ -76,7 +78,9 @@ public void testCircuitBreakerCommonConfig() {
assertThat(circuitBreakerConfig.reactorCircuitBreakerAspect()).isNotNull();
assertThat(circuitBreakerConfig.rxJava2CircuitBreakerAspect()).isNotNull();
assertThat(circuitBreakerConfig.circuitBreakerRegistry(new DefaultEventConsumerRegistry<>(),
new CompositeRegistryEventConsumer<>(emptyList()))).isNotNull();
new CompositeRegistryEventConsumer<>(emptyList()),
new CompositeCircuitBreakerCustomizer(Collections.singletonList(new TestCustomizer()))))
.isNotNull();
assertThat(circuitBreakerConfig
.circuitBreakerAspect(CircuitBreakerRegistry.ofDefaults(), Collections.emptyList(),
new FallbackDecorators(Arrays.asList(new CompletionStageFallbackDecorator()))));
Expand Down Expand Up @@ -127,7 +131,20 @@ public CircuitBreakerConfig(
CircuitBreakerConfigurationProperties circuitBreakerProperties) {
super(circuitBreakerProperties);
}
}

class TestCustomizer implements CircuitBreakerConfigCustomizer {

@Override
public void customize(
io.github.resilience4j.circuitbreaker.CircuitBreakerConfig.Builder builder) {
builder.slidingWindowSize(3000);
}

@Override
public String name() {
return "backendCustom";
}
}

class RetryConfigurationOnMissingBean extends AbstractRetryConfigurationOnMissingBean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,17 @@
import io.github.resilience4j.circuitbreaker.event.CircuitBreakerEvent;
import io.github.resilience4j.common.IntegerToDurationConverter;
import io.github.resilience4j.common.StringToDurationConverter;
import io.github.resilience4j.common.circuitbreaker.configuration.CircuitBreakerConfigCustomizer;
import io.github.resilience4j.common.circuitbreaker.configuration.CompositeCircuitBreakerCustomizer;
import io.github.resilience4j.consumer.EventConsumerRegistry;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import java.util.List;

@Configuration
@Import({IntegerToDurationConverter.class, StringToDurationConverter.class})
public class CircuitBreakerConfigurationOnMissingBean extends
Expand All @@ -39,4 +45,11 @@ public EventConsumerRegistry<CircuitBreakerEvent> eventConsumerRegistry() {
return circuitBreakerConfiguration.eventConsumerRegistry();
}

@Bean
@ConditionalOnMissingBean
public CompositeCircuitBreakerCustomizer circuitBreakerCustomizerFinder(
@Autowired(required = false) List<CircuitBreakerConfigCustomizer> customizers) {
return new CompositeCircuitBreakerCustomizer(customizers);
}

}
Loading

0 comments on commit 7c5c724

Please sign in to comment.