diff --git a/resilience4j-framework-common/src/main/java/io/github/resilience4j/common/ratelimiter/configuration/RateLimiterConfigurationProperties.java b/resilience4j-framework-common/src/main/java/io/github/resilience4j/common/ratelimiter/configuration/RateLimiterConfigurationProperties.java index c17145b146..57bd32ddf3 100644 --- a/resilience4j-framework-common/src/main/java/io/github/resilience4j/common/ratelimiter/configuration/RateLimiterConfigurationProperties.java +++ b/resilience4j-framework-common/src/main/java/io/github/resilience4j/common/ratelimiter/configuration/RateLimiterConfigurationProperties.java @@ -25,12 +25,17 @@ import java.time.Duration; import java.util.HashMap; import java.util.Map; +import java.util.Optional; public class RateLimiterConfigurationProperties { private Map instances = new HashMap<>(); private Map configs = new HashMap<>(); + public Optional findRateLimiterProperties(String name) { + return Optional.ofNullable(instances.get(name)); + } + public RateLimiterConfig createRateLimiterConfig(@Nullable InstanceProperties instanceProperties) { if (instanceProperties == null) { return RateLimiterConfig.ofDefaults(); diff --git a/resilience4j-spring-boot-common/src/main/java/io/github/resilience4j/circuitbreaker/autoconfigure/AbstractCircuitBreakerConfigurationOnMissingBean.java b/resilience4j-spring-boot-common/src/main/java/io/github/resilience4j/circuitbreaker/autoconfigure/AbstractCircuitBreakerConfigurationOnMissingBean.java index b2e28855a3..4e7a2519e1 100644 --- a/resilience4j-spring-boot-common/src/main/java/io/github/resilience4j/circuitbreaker/autoconfigure/AbstractCircuitBreakerConfigurationOnMissingBean.java +++ b/resilience4j-spring-boot-common/src/main/java/io/github/resilience4j/circuitbreaker/autoconfigure/AbstractCircuitBreakerConfigurationOnMissingBean.java @@ -15,7 +15,6 @@ */ package io.github.resilience4j.circuitbreaker.autoconfigure; -import io.github.resilience4j.circuitbreaker.CircuitBreaker; import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; import io.github.resilience4j.circuitbreaker.configure.*; import io.github.resilience4j.circuitbreaker.event.CircuitBreakerEvent; @@ -54,18 +53,12 @@ public CircuitBreakerRegistry circuitBreakerRegistry(EventConsumerRegistry createHealthIndicatorForCircuitBreaker(event.getAddedEntry(), circuitBreakerProperties)); - // Initialize backends that were initially configured. circuitBreakerConfiguration.initCircuitBreakerRegistry(circuitBreakerRegistry); return circuitBreakerRegistry; } - protected abstract void createHealthIndicatorForCircuitBreaker(CircuitBreaker circuitBreaker, CircuitBreakerConfigurationProperties circuitBreakerProperties); - @Bean @ConditionalOnMissingBean @Conditional(value = {AspectJOnClasspathCondition.class}) diff --git a/resilience4j-spring-boot-common/src/main/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakerHealthIndicator.java b/resilience4j-spring-boot-common/src/main/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakersHealthIndicator.java similarity index 61% rename from resilience4j-spring-boot-common/src/main/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakerHealthIndicator.java rename to resilience4j-spring-boot-common/src/main/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakersHealthIndicator.java index 4fa6489e5a..c5e8aa651b 100644 --- a/resilience4j-spring-boot-common/src/main/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakerHealthIndicator.java +++ b/resilience4j-spring-boot-common/src/main/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakersHealthIndicator.java @@ -18,15 +18,19 @@ import io.github.resilience4j.circuitbreaker.CircuitBreaker; import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; +import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; +import io.github.resilience4j.circuitbreaker.configure.CircuitBreakerConfigurationProperties; import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthAggregator; import org.springframework.boot.actuate.health.HealthIndicator; -import java.util.Optional; +import java.util.Map; +import java.util.stream.Collectors; /** * A Spring Boot health indicators which adds the state of a CircuitBreaker and it's metrics to the health endpoints */ -public class CircuitBreakerHealthIndicator implements HealthIndicator { +public class CircuitBreakersHealthIndicator implements HealthIndicator { private static final String FAILURE_RATE = "failureRate"; private static final String SLOW_CALL_RATE = "slowCallRate"; @@ -37,20 +41,35 @@ public class CircuitBreakerHealthIndicator implements HealthIndicator { private static final String SLOW_CALLS = "slowCalls"; private static final String NOT_PERMITTED = "notPermittedCalls"; private static final String STATE = "state"; - private final CircuitBreaker circuitBreaker; - public CircuitBreakerHealthIndicator(CircuitBreaker circuitBreaker) { - this.circuitBreaker = circuitBreaker; + private final CircuitBreakerRegistry circuitBreakerRegistry; + private final CircuitBreakerConfigurationProperties circuitBreakerProperties; + private final HealthAggregator healthAggregator; + + public CircuitBreakersHealthIndicator(CircuitBreakerRegistry circuitBreakerRegistry, + CircuitBreakerConfigurationProperties circuitBreakerProperties, + HealthAggregator healthAggregator) { + this.circuitBreakerRegistry = circuitBreakerRegistry; + this.circuitBreakerProperties = circuitBreakerProperties; + this.healthAggregator = healthAggregator; } @Override public Health health() { - return Optional.of(circuitBreaker) - .map(this::mapBackendMonitorState) - .orElse(Health.up().build()); + Map healths = circuitBreakerRegistry.getAllCircuitBreakers().toJavaStream() + .filter(this::isRegisterHealthIndicator) + .collect(Collectors.toMap(CircuitBreaker::getName, CircuitBreakersHealthIndicator::mapBackendMonitorState)); + + return healthAggregator.aggregate(healths); + } + + private boolean isRegisterHealthIndicator(CircuitBreaker circuitBreaker) { + return circuitBreakerProperties.findCircuitBreakerProperties(circuitBreaker.getName()) + .map(io.github.resilience4j.common.circuitbreaker.configuration.CircuitBreakerConfigurationProperties.InstanceProperties::getRegisterHealthIndicator) + .orElse(true); } - private Health mapBackendMonitorState(CircuitBreaker circuitBreaker) { + private static Health mapBackendMonitorState(CircuitBreaker circuitBreaker) { switch (circuitBreaker.getState()) { case CLOSED: return addDetails(Health.up(), circuitBreaker).build(); @@ -63,7 +82,7 @@ private Health mapBackendMonitorState(CircuitBreaker circuitBreaker) { } } - private Health.Builder addDetails(Health.Builder builder, CircuitBreaker circuitBreaker) { + private static Health.Builder addDetails(Health.Builder builder, CircuitBreaker circuitBreaker) { CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics(); CircuitBreakerConfig config = circuitBreaker.getCircuitBreakerConfig(); builder.withDetail(FAILURE_RATE, metrics.getFailureRate() + "%") diff --git a/resilience4j-spring-boot-common/src/main/java/io/github/resilience4j/ratelimiter/monitoring/health/RateLimiterHealthIndicator.java b/resilience4j-spring-boot-common/src/main/java/io/github/resilience4j/ratelimiter/monitoring/health/RateLimitersHealthIndicator.java similarity index 53% rename from resilience4j-spring-boot-common/src/main/java/io/github/resilience4j/ratelimiter/monitoring/health/RateLimiterHealthIndicator.java rename to resilience4j-spring-boot-common/src/main/java/io/github/resilience4j/ratelimiter/monitoring/health/RateLimitersHealthIndicator.java index 0950751cc6..4a565aa29d 100644 --- a/resilience4j-spring-boot-common/src/main/java/io/github/resilience4j/ratelimiter/monitoring/health/RateLimiterHealthIndicator.java +++ b/resilience4j-spring-boot-common/src/main/java/io/github/resilience4j/ratelimiter/monitoring/health/RateLimitersHealthIndicator.java @@ -16,31 +16,57 @@ package io.github.resilience4j.ratelimiter.monitoring.health; import io.github.resilience4j.ratelimiter.RateLimiter; +import io.github.resilience4j.ratelimiter.RateLimiterRegistry; +import io.github.resilience4j.ratelimiter.configure.RateLimiterConfigurationProperties; import io.github.resilience4j.ratelimiter.internal.AtomicRateLimiter; import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthAggregator; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.boot.actuate.health.Status; -public class RateLimiterHealthIndicator implements HealthIndicator { +import java.util.Map; +import java.util.stream.Collectors; - private final RateLimiter rateLimiter; - private final long timeoutInNanos; +public class RateLimitersHealthIndicator implements HealthIndicator { - public RateLimiterHealthIndicator(RateLimiter rateLimiter) { - this.rateLimiter = rateLimiter; - this.timeoutInNanos = rateLimiter.getRateLimiterConfig().getTimeoutDuration().toNanos(); + private final RateLimiterRegistry rateLimiterRegistry; + private final RateLimiterConfigurationProperties rateLimiterProperties; + private final HealthAggregator healthAggregator; + + public RateLimitersHealthIndicator(RateLimiterRegistry rateLimiterRegistry, + RateLimiterConfigurationProperties rateLimiterProperties, + HealthAggregator healthAggregator) { + this.rateLimiterRegistry = rateLimiterRegistry; + this.rateLimiterProperties = rateLimiterProperties; + this.healthAggregator = healthAggregator; } @Override public Health health() { + Map healths = rateLimiterRegistry.getAllRateLimiters().toJavaStream() + .filter(this::isRegisterHealthIndicator) + .collect(Collectors.toMap(RateLimiter::getName, this::mapRateLimiterHealth)); + + return healthAggregator.aggregate(healths); + } + + private boolean isRegisterHealthIndicator(RateLimiter rateLimiter) { + return rateLimiterProperties.findRateLimiterProperties(rateLimiter.getName()) + .map(io.github.resilience4j.common.ratelimiter.configuration.RateLimiterConfigurationProperties.InstanceProperties::getRegisterHealthIndicator) + .orElse(true); + } + + private Health mapRateLimiterHealth(RateLimiter rateLimiter) { RateLimiter.Metrics metrics = rateLimiter.getMetrics(); int availablePermissions = metrics.getAvailablePermissions(); int numberOfWaitingThreads = metrics.getNumberOfWaitingThreads(); + long timeoutInNanos = rateLimiter.getRateLimiterConfig().getTimeoutDuration().toNanos(); + if (availablePermissions > 0 || numberOfWaitingThreads == 0) { return rateLimiterHealth(Status.UP, availablePermissions, numberOfWaitingThreads); } if (rateLimiter instanceof AtomicRateLimiter) { - AtomicRateLimiter atomicRateLimiter = (AtomicRateLimiter) this.rateLimiter; + AtomicRateLimiter atomicRateLimiter = (AtomicRateLimiter) rateLimiter; AtomicRateLimiter.AtomicRateLimiterMetrics detailedMetrics = atomicRateLimiter.getDetailedMetrics(); if (detailedMetrics.getNanosToWait() > timeoutInNanos) { return rateLimiterHealth(Status.DOWN, availablePermissions, numberOfWaitingThreads); @@ -49,10 +75,10 @@ public Health health() { return rateLimiterHealth(Status.UNKNOWN, availablePermissions, numberOfWaitingThreads); } - private Health rateLimiterHealth(Status status, int availablePermissions, int numberOfWaitingThreads) { + private static Health rateLimiterHealth(Status status, int availablePermissions, int numberOfWaitingThreads) { return Health.status(status) .withDetail("availablePermissions", availablePermissions) .withDetail("numberOfWaitingThreads", numberOfWaitingThreads) .build(); } -} \ No newline at end of file +} diff --git a/resilience4j-spring-boot-common/src/test/java/io/github/resilience4j/SpringBootCommonTest.java b/resilience4j-spring-boot-common/src/test/java/io/github/resilience4j/SpringBootCommonTest.java index 11ba72d6ab..3d5b0d5209 100644 --- a/resilience4j-spring-boot-common/src/test/java/io/github/resilience4j/SpringBootCommonTest.java +++ b/resilience4j-spring-boot-common/src/test/java/io/github/resilience4j/SpringBootCommonTest.java @@ -19,7 +19,6 @@ import io.github.resilience4j.bulkhead.ThreadPoolBulkheadRegistry; import io.github.resilience4j.bulkhead.autoconfigure.AbstractBulkheadConfigurationOnMissingBean; import io.github.resilience4j.bulkhead.configure.BulkheadConfigurationProperties; -import io.github.resilience4j.circuitbreaker.CircuitBreaker; import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; import io.github.resilience4j.circuitbreaker.autoconfigure.AbstractCircuitBreakerConfigurationOnMissingBean; import io.github.resilience4j.circuitbreaker.configure.CircuitBreakerConfigurationProperties; @@ -93,10 +92,6 @@ public CircuitBreakerConfig(CircuitBreakerConfigurationProperties circuitBreaker super(circuitBreakerProperties); } - @Override - protected void createHealthIndicatorForCircuitBreaker(CircuitBreaker circuitBreaker, CircuitBreakerConfigurationProperties circuitBreakerProperties) { - - } } class RetryConfigurationOnMissingBean extends AbstractRetryConfigurationOnMissingBean { diff --git a/resilience4j-spring-boot-common/src/test/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakerHealthIndicatorTest.java b/resilience4j-spring-boot-common/src/test/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakerHealthIndicatorTest.java deleted file mode 100644 index b6a59b913e..0000000000 --- a/resilience4j-spring-boot-common/src/test/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakerHealthIndicatorTest.java +++ /dev/null @@ -1,98 +0,0 @@ -package io.github.resilience4j.circuitbreaker.monitoring.health; - -import io.github.resilience4j.circuitbreaker.CircuitBreaker; -import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; -import org.junit.Test; -import org.springframework.boot.actuate.health.Health; -import org.springframework.boot.actuate.health.Status; - -import java.util.AbstractMap.SimpleEntry; -import java.util.HashMap; -import java.util.Map; - -import static io.github.resilience4j.circuitbreaker.CircuitBreaker.State.*; -import static org.assertj.core.api.BDDAssertions.then; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -/** - * @author bstorozhuk - */ -public class CircuitBreakerHealthIndicatorTest { - - @Test - public void healthMetricsAndConfig() { - // given - CircuitBreakerConfig config = mock(CircuitBreakerConfig.class); - CircuitBreaker.Metrics metrics = mock(CircuitBreaker.Metrics.class); - CircuitBreaker circuitBreaker = mock(CircuitBreaker.class); - CircuitBreakerHealthIndicator healthIndicator = new CircuitBreakerHealthIndicator(circuitBreaker); - - //when - when(config.getFailureRateThreshold()).thenReturn(30f); - when(metrics.getFailureRate()).thenReturn(20f); - when(metrics.getSlowCallRate()).thenReturn(20f); - when(config.getSlowCallRateThreshold()).thenReturn(50f); - when(metrics.getNumberOfBufferedCalls()).thenReturn(100); - when(metrics.getNumberOfFailedCalls()).thenReturn(20); - when(metrics.getNumberOfSlowCalls()).thenReturn(20); - when(metrics.getNumberOfNotPermittedCalls()).thenReturn(0L); - - when(circuitBreaker.getCircuitBreakerConfig()).thenReturn(config); - when(circuitBreaker.getMetrics()).thenReturn(metrics); - when(circuitBreaker.getState()).thenReturn(CLOSED, OPEN, HALF_OPEN, CLOSED); - - // then - Health health = healthIndicator.health(); - then(health.getStatus()).isEqualTo(Status.UP); - then(health.getDetails()) - .contains( - entry("failureRate", "20.0%"), - entry("slowCallRate", "20.0%"), - entry("slowCallRateThreshold", "50.0%"), - entry("failureRateThreshold", "30.0%"), - entry("bufferedCalls", 100), - entry("slowCalls", 20), - entry("failedCalls", 20), - entry("notPermittedCalls", 0L) - ); - } - - @Test - public void testHealthStatus() { - Map expectedStateToStatusMap = new HashMap<>(); - expectedStateToStatusMap.put(OPEN, Status.DOWN); - expectedStateToStatusMap.put(HALF_OPEN, Status.UNKNOWN); - expectedStateToStatusMap.put(CLOSED, Status.UP); - - // given - CircuitBreakerConfig config = mock(CircuitBreakerConfig.class); - CircuitBreaker.Metrics metrics = mock(CircuitBreaker.Metrics.class); - CircuitBreaker circuitBreaker = mock(CircuitBreaker.class); - - when(circuitBreaker.getCircuitBreakerConfig()).thenReturn(config); - when(circuitBreaker.getMetrics()).thenReturn(metrics); - - expectedStateToStatusMap.forEach((state, status) -> assertStatusForGivenState(circuitBreaker, state, status)); - } - - private void assertStatusForGivenState(CircuitBreaker circuitBreaker, CircuitBreaker.State givenState, Status expectedStatus) { - // given - when(circuitBreaker.getState()).thenReturn(givenState); - CircuitBreakerHealthIndicator healthIndicator = new CircuitBreakerHealthIndicator(circuitBreaker); - - // when - Health health = healthIndicator.health(); - - // then - then(health.getStatus()).isEqualTo(expectedStatus); - then(health.getDetails()) - .contains( - entry("state", givenState) - ); - } - - private SimpleEntry entry(String key, Object value) { - return new SimpleEntry<>(key, value); - } -} \ No newline at end of file diff --git a/resilience4j-spring-boot-common/src/test/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakersHealthIndicatorTest.java b/resilience4j-spring-boot-common/src/test/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakersHealthIndicatorTest.java new file mode 100644 index 0000000000..0c9e709b7e --- /dev/null +++ b/resilience4j-spring-boot-common/src/test/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakersHealthIndicatorTest.java @@ -0,0 +1,147 @@ +package io.github.resilience4j.circuitbreaker.monitoring.health; + +import io.github.resilience4j.circuitbreaker.CircuitBreaker; +import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig; +import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; +import io.github.resilience4j.circuitbreaker.configure.CircuitBreakerConfigurationProperties; +import io.vavr.collection.Array; +import org.junit.Test; +import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.HealthAggregator; +import org.springframework.boot.actuate.health.OrderedHealthAggregator; +import org.springframework.boot.actuate.health.Status; + +import java.util.AbstractMap.SimpleEntry; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +import static io.github.resilience4j.circuitbreaker.CircuitBreaker.State.*; +import static org.assertj.core.api.BDDAssertions.then; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * @author bstorozhuk + */ +public class CircuitBreakersHealthIndicatorTest { + + @Test + public void healthMetricsAndConfig() { + // given + CircuitBreakerConfig config = mock(CircuitBreakerConfig.class); + CircuitBreakerRegistry registry = mock(CircuitBreakerRegistry.class); + CircuitBreaker.Metrics metrics = mock(CircuitBreaker.Metrics.class); + CircuitBreaker circuitBreaker = mock(CircuitBreaker.class); + io.github.resilience4j.common.circuitbreaker.configuration.CircuitBreakerConfigurationProperties.InstanceProperties instanceProperties = + mock(io.github.resilience4j.common.circuitbreaker.configuration.CircuitBreakerConfigurationProperties.InstanceProperties.class); + CircuitBreakerConfigurationProperties circuitBreakerProperties = mock(CircuitBreakerConfigurationProperties.class); + HealthAggregator healthAggregator = new OrderedHealthAggregator(); + CircuitBreakersHealthIndicator healthIndicator = + new CircuitBreakersHealthIndicator(registry, circuitBreakerProperties, healthAggregator); + + //when + when(config.getFailureRateThreshold()).thenReturn(30f); + when(metrics.getFailureRate()).thenReturn(20f); + when(metrics.getSlowCallRate()).thenReturn(20f); + when(config.getSlowCallRateThreshold()).thenReturn(50f); + when(metrics.getNumberOfBufferedCalls()).thenReturn(100); + when(metrics.getNumberOfFailedCalls()).thenReturn(20); + when(metrics.getNumberOfSlowCalls()).thenReturn(20); + when(metrics.getNumberOfNotPermittedCalls()).thenReturn(0L); + + when(registry.getAllCircuitBreakers()).thenReturn(Array.of(circuitBreaker)); + when(circuitBreaker.getName()).thenReturn("test"); + when(circuitBreakerProperties.findCircuitBreakerProperties("test")).thenReturn(Optional.of(instanceProperties)); + when(instanceProperties.getRegisterHealthIndicator()).thenReturn(true); + when(circuitBreaker.getMetrics()).thenReturn(metrics); + when(circuitBreaker.getCircuitBreakerConfig()).thenReturn(config); + when(circuitBreaker.getState()).thenReturn(CLOSED, OPEN, HALF_OPEN, CLOSED); + + // then + Health health = healthIndicator.health(); + then(health.getStatus()).isEqualTo(Status.UP); + then(health.getDetails()).containsKey("test"); + then(health.getDetails().get("test")).isInstanceOf(Health.class); + then(((Health) health.getDetails().get("test")).getDetails()) + .contains( + entry("failureRate", "20.0%"), + entry("slowCallRate", "20.0%"), + entry("slowCallRateThreshold", "50.0%"), + entry("failureRateThreshold", "30.0%"), + entry("bufferedCalls", 100), + entry("slowCalls", 20), + entry("failedCalls", 20), + entry("notPermittedCalls", 0L) + ); + } + + @Test + public void testHealthStatus() { + CircuitBreaker openCircuitBreaker = mock(CircuitBreaker.class); + CircuitBreaker halfOpenCircuitBreaker = mock(CircuitBreaker.class); + CircuitBreaker closeCircuitBreaker = mock(CircuitBreaker.class); + + Map expectedStateToCircuitBreaker = new HashMap<>(); + expectedStateToCircuitBreaker.put(OPEN, openCircuitBreaker); + expectedStateToCircuitBreaker.put(HALF_OPEN, halfOpenCircuitBreaker); + expectedStateToCircuitBreaker.put(CLOSED, closeCircuitBreaker); + io.github.resilience4j.common.circuitbreaker.configuration.CircuitBreakerConfigurationProperties.InstanceProperties instanceProperties = + mock(io.github.resilience4j.common.circuitbreaker.configuration.CircuitBreakerConfigurationProperties.InstanceProperties.class); + CircuitBreakerConfigurationProperties circuitBreakerProperties = mock(CircuitBreakerConfigurationProperties.class); + + // given + CircuitBreakerRegistry registry = mock(CircuitBreakerRegistry.class); + CircuitBreakerConfig config = mock(CircuitBreakerConfig.class); + CircuitBreaker.Metrics metrics = mock(CircuitBreaker.Metrics.class); + + // when + when(registry.getAllCircuitBreakers()).thenReturn(Array.ofAll(expectedStateToCircuitBreaker.values())); + expectedStateToCircuitBreaker.forEach( + (state, circuitBreaker) -> setCircuitBreakerWhen(state, circuitBreaker, config, metrics, instanceProperties, circuitBreakerProperties)); + + HealthAggregator healthAggregator = new OrderedHealthAggregator(); + CircuitBreakersHealthIndicator healthIndicator = + new CircuitBreakersHealthIndicator(registry, circuitBreakerProperties, healthAggregator); + + + // then + Health health = healthIndicator.health(); + + then(health.getStatus()).isEqualTo(Status.DOWN); + then(health.getDetails()).containsKeys(OPEN.name(), HALF_OPEN.name(), CLOSED.name()); + + assertState(OPEN, Status.DOWN, health.getDetails()); + assertState(HALF_OPEN, Status.UNKNOWN, health.getDetails()); + assertState(CLOSED, Status.UP, health.getDetails()); + + } + + private void setCircuitBreakerWhen(CircuitBreaker.State givenState, CircuitBreaker circuitBreaker, + CircuitBreakerConfig config, CircuitBreaker.Metrics metrics, + io.github.resilience4j.common.circuitbreaker.configuration.CircuitBreakerConfigurationProperties.InstanceProperties instanceProperties, + CircuitBreakerConfigurationProperties circuitBreakerProperties) { + + when(circuitBreaker.getName()).thenReturn(givenState.name()); + when(circuitBreaker.getState()).thenReturn(givenState); + when(circuitBreaker.getCircuitBreakerConfig()).thenReturn(config); + when(circuitBreaker.getMetrics()).thenReturn(metrics); + when(circuitBreakerProperties.findCircuitBreakerProperties("test")).thenReturn(Optional.of(instanceProperties)); + when(instanceProperties.getRegisterHealthIndicator()).thenReturn(true); + } + + private void assertState(CircuitBreaker.State givenState, Status expectedStatus, Map details) { + + then(details.get(givenState.name())).isInstanceOf(Health.class); + Health health = (Health) details.get(givenState.name()); + then(health.getStatus()).isEqualTo(expectedStatus); + then(health.getDetails()) + .contains( + entry("state", givenState) + ); + } + + private SimpleEntry entry(String key, Object value) { + return new SimpleEntry<>(key, value); + } +} \ No newline at end of file diff --git a/resilience4j-spring-boot-common/src/test/java/io/github/resilience4j/ratelimiter/monitoring/health/RateLimiterHealthIndicatorTest.java b/resilience4j-spring-boot-common/src/test/java/io/github/resilience4j/ratelimiter/monitoring/health/RateLimitersHealthIndicatorTest.java similarity index 56% rename from resilience4j-spring-boot-common/src/test/java/io/github/resilience4j/ratelimiter/monitoring/health/RateLimiterHealthIndicatorTest.java rename to resilience4j-spring-boot-common/src/test/java/io/github/resilience4j/ratelimiter/monitoring/health/RateLimitersHealthIndicatorTest.java index 25deefa34e..172c961955 100644 --- a/resilience4j-spring-boot-common/src/test/java/io/github/resilience4j/ratelimiter/monitoring/health/RateLimiterHealthIndicatorTest.java +++ b/resilience4j-spring-boot-common/src/test/java/io/github/resilience4j/ratelimiter/monitoring/health/RateLimitersHealthIndicatorTest.java @@ -1,13 +1,18 @@ package io.github.resilience4j.ratelimiter.monitoring.health; import io.github.resilience4j.ratelimiter.RateLimiterConfig; +import io.github.resilience4j.ratelimiter.RateLimiterRegistry; +import io.github.resilience4j.ratelimiter.configure.RateLimiterConfigurationProperties; import io.github.resilience4j.ratelimiter.internal.AtomicRateLimiter; +import io.vavr.collection.Array; import org.junit.Test; import org.springframework.boot.actuate.health.Health; +import org.springframework.boot.actuate.health.OrderedHealthAggregator; import org.springframework.boot.actuate.health.Status; import java.time.Duration; import java.util.AbstractMap.SimpleEntry; +import java.util.Optional; import static org.assertj.core.api.BDDAssertions.then; import static org.mockito.Mockito.mock; @@ -16,19 +21,26 @@ /** * @author bstorozhuk */ -public class RateLimiterHealthIndicatorTest { +public class RateLimitersHealthIndicatorTest { @Test public void health() throws Exception { // given RateLimiterConfig config = mock(RateLimiterConfig.class); AtomicRateLimiter.AtomicRateLimiterMetrics metrics = mock(AtomicRateLimiter.AtomicRateLimiterMetrics.class); AtomicRateLimiter rateLimiter = mock(AtomicRateLimiter.class); + RateLimiterRegistry rateLimiterRegistry = mock(RateLimiterRegistry.class); + io.github.resilience4j.common.ratelimiter.configuration.RateLimiterConfigurationProperties.InstanceProperties instanceProperties = + mock(io.github.resilience4j.common.ratelimiter.configuration.RateLimiterConfigurationProperties.InstanceProperties.class); + RateLimiterConfigurationProperties rateLimiterProperties = mock(RateLimiterConfigurationProperties.class); //when - when(rateLimiter.getRateLimiterConfig()).thenReturn(config); + when(rateLimiter.getName()).thenReturn("test"); + when(rateLimiterProperties.findRateLimiterProperties("test")).thenReturn(Optional.of(instanceProperties)); + when(instanceProperties.getRegisterHealthIndicator()).thenReturn(true); when(rateLimiter.getMetrics()).thenReturn(metrics); when(rateLimiter.getDetailedMetrics()).thenReturn(metrics); + when(rateLimiterRegistry.getAllRateLimiters()).thenReturn(Array.of(rateLimiter)); when(config.getTimeoutDuration()).thenReturn(Duration.ofNanos(30L)); @@ -40,7 +52,9 @@ public void health() throws Exception { .thenReturn(20L, 40L); // then - RateLimiterHealthIndicator healthIndicator = new RateLimiterHealthIndicator(rateLimiter); + OrderedHealthAggregator healthAggregator = new OrderedHealthAggregator(); + RateLimitersHealthIndicator healthIndicator = + new RateLimitersHealthIndicator(rateLimiterRegistry, rateLimiterProperties, healthAggregator); Health health = healthIndicator.health(); then(health.getStatus()).isEqualTo(Status.UP); @@ -51,7 +65,8 @@ public void health() throws Exception { health = healthIndicator.health(); then(health.getStatus()).isEqualTo(Status.DOWN); - then(health.getDetails()) + then(health.getDetails().get("test")).isInstanceOf(Health.class); + then(((Health) health.getDetails().get("test")).getDetails()) .contains( entry("availablePermissions", -2), entry("numberOfWaitingThreads", 2) diff --git a/resilience4j-spring-boot/src/main/java/io/github/resilience4j/bulkhead/autoconfigure/BulkheadAutoConfiguration.java b/resilience4j-spring-boot/src/main/java/io/github/resilience4j/bulkhead/autoconfigure/BulkheadAutoConfiguration.java index 80b4f4a938..2e1303dcca 100644 --- a/resilience4j-spring-boot/src/main/java/io/github/resilience4j/bulkhead/autoconfigure/BulkheadAutoConfiguration.java +++ b/resilience4j-spring-boot/src/main/java/io/github/resilience4j/bulkhead/autoconfigure/BulkheadAutoConfiguration.java @@ -42,15 +42,19 @@ @AutoConfigureBefore(EndpointAutoConfiguration.class) public class BulkheadAutoConfiguration { - @Bean + @Configuration @ConditionalOnClass(value = {Endpoint.class}) - public BulkheadEndpoint bulkheadEndpoint(BulkheadRegistry bulkheadRegistry, ThreadPoolBulkheadRegistry threadPoolBulkheadRegistry) { - return new BulkheadEndpoint(bulkheadRegistry, threadPoolBulkheadRegistry); - } + public static class BulkheadEndpointConfiguration { + + @Bean + public BulkheadEndpoint bulkheadEndpoint(BulkheadRegistry bulkheadRegistry, ThreadPoolBulkheadRegistry threadPoolBulkheadRegistry) { + return new BulkheadEndpoint(bulkheadRegistry, threadPoolBulkheadRegistry); + } + + @Bean + public BulkheadEventsEndpoint bulkheadEventsEndpoint(EventConsumerRegistry eventConsumerRegistry) { + return new BulkheadEventsEndpoint(eventConsumerRegistry); + } - @Bean - @ConditionalOnClass(value = {Endpoint.class}) - public BulkheadEventsEndpoint bulkheadEventsEndpoint(EventConsumerRegistry eventConsumerRegistry) { - return new BulkheadEventsEndpoint(eventConsumerRegistry); } } diff --git a/resilience4j-spring-boot/src/main/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakerAutoConfiguration.java b/resilience4j-spring-boot/src/main/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakerAutoConfiguration.java index cbe8719159..7babe3c106 100644 --- a/resilience4j-spring-boot/src/main/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakerAutoConfiguration.java +++ b/resilience4j-spring-boot/src/main/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakerAutoConfiguration.java @@ -42,16 +42,19 @@ @AutoConfigureBefore(EndpointAutoConfiguration.class) public class CircuitBreakerAutoConfiguration { - @Bean + @Configuration @ConditionalOnClass(value = {Endpoint.class}) - public CircuitBreakerEndpoint circuitBreakerEndpoint(CircuitBreakerRegistry circuitBreakerRegistry) { - return new CircuitBreakerEndpoint(circuitBreakerRegistry); - } + public static class CircuitBreakerEndpointConfiguration { - @Bean - @ConditionalOnClass(value = {Endpoint.class}) - public CircuitBreakerEventsEndpoint circuitBreakerEventsEndpoint(EventConsumerRegistry eventConsumerRegistry) { - return new CircuitBreakerEventsEndpoint(eventConsumerRegistry); - } + @Bean + public CircuitBreakerEndpoint circuitBreakerEndpoint(CircuitBreakerRegistry circuitBreakerRegistry) { + return new CircuitBreakerEndpoint(circuitBreakerRegistry); + } + @Bean + public CircuitBreakerEventsEndpoint circuitBreakerEventsEndpoint(EventConsumerRegistry eventConsumerRegistry) { + return new CircuitBreakerEventsEndpoint(eventConsumerRegistry); + } + + } } diff --git a/resilience4j-spring-boot/src/main/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakerConfigurationOnMissingBean.java b/resilience4j-spring-boot/src/main/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakerConfigurationOnMissingBean.java index 091f6222a0..5948168602 100644 --- a/resilience4j-spring-boot/src/main/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakerConfigurationOnMissingBean.java +++ b/resilience4j-spring-boot/src/main/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakerConfigurationOnMissingBean.java @@ -15,29 +15,21 @@ */ package io.github.resilience4j.circuitbreaker.autoconfigure; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; - -import io.github.resilience4j.circuitbreaker.CircuitBreaker; import io.github.resilience4j.circuitbreaker.configure.CircuitBreakerConfigurationProperties; import io.github.resilience4j.circuitbreaker.event.CircuitBreakerEvent; -import io.github.resilience4j.circuitbreaker.monitoring.health.CircuitBreakerHealthIndicator; import io.github.resilience4j.common.IntegerToDurationConverter; import io.github.resilience4j.common.StringToDurationConverter; import io.github.resilience4j.consumer.EventConsumerRegistry; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; @Configuration @Import({IntegerToDurationConverter.class, StringToDurationConverter.class}) public class CircuitBreakerConfigurationOnMissingBean extends AbstractCircuitBreakerConfigurationOnMissingBean { - private final ConfigurableBeanFactory beanFactory; - - public CircuitBreakerConfigurationOnMissingBean(CircuitBreakerConfigurationProperties circuitBreakerProperties, - ConfigurableBeanFactory beanFactory) { + public CircuitBreakerConfigurationOnMissingBean(CircuitBreakerConfigurationProperties circuitBreakerProperties) { super(circuitBreakerProperties); - this.beanFactory = beanFactory; } @Bean @@ -45,17 +37,4 @@ public EventConsumerRegistry eventConsumerRegistry() { return circuitBreakerConfiguration.eventConsumerRegistry(); } - protected void createHealthIndicatorForCircuitBreaker(CircuitBreaker circuitBreaker, CircuitBreakerConfigurationProperties circuitBreakerProperties) { - boolean registerHealthIndicator = circuitBreakerProperties.findCircuitBreakerProperties(circuitBreaker.getName()) - .map(io.github.resilience4j.common.circuitbreaker.configuration.CircuitBreakerConfigurationProperties.InstanceProperties::getRegisterHealthIndicator) - .orElse(true); - - if (registerHealthIndicator) { - CircuitBreakerHealthIndicator healthIndicator = new CircuitBreakerHealthIndicator(circuitBreaker); - beanFactory.registerSingleton( - circuitBreaker.getName() + "CircuitBreakerHealthIndicator", - healthIndicator - ); - } - } } diff --git a/resilience4j-spring-boot/src/main/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakersHealthIndicatorAutoConfiguration.java b/resilience4j-spring-boot/src/main/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakersHealthIndicatorAutoConfiguration.java new file mode 100644 index 0000000000..6976a05309 --- /dev/null +++ b/resilience4j-spring-boot/src/main/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakersHealthIndicatorAutoConfiguration.java @@ -0,0 +1,31 @@ +package io.github.resilience4j.circuitbreaker.autoconfigure; + +import io.github.resilience4j.circuitbreaker.CircuitBreaker; +import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; +import io.github.resilience4j.circuitbreaker.configure.CircuitBreakerConfigurationProperties; +import io.github.resilience4j.circuitbreaker.monitoring.health.CircuitBreakersHealthIndicator; +import org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration; +import org.springframework.boot.actuate.health.HealthAggregator; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConditionalOnClass({CircuitBreaker.class, HealthIndicator.class}) +@AutoConfigureAfter(CircuitBreakerAutoConfiguration.class) +@AutoConfigureBefore(HealthIndicatorAutoConfiguration.class) +public class CircuitBreakersHealthIndicatorAutoConfiguration { + + @Bean + @ConditionalOnMissingBean(name = "circuitBreakersHealthIndicator") + public CircuitBreakersHealthIndicator circuitBreakersHealthIndicator(CircuitBreakerRegistry circuitBreakerRegistry, + CircuitBreakerConfigurationProperties circuitBreakerProperties, + HealthAggregator healthAggregator) { + return new CircuitBreakersHealthIndicator(circuitBreakerRegistry, circuitBreakerProperties, healthAggregator); + } + +} diff --git a/resilience4j-spring-boot/src/main/java/io/github/resilience4j/ratelimiter/autoconfigure/RateLimiterAutoConfiguration.java b/resilience4j-spring-boot/src/main/java/io/github/resilience4j/ratelimiter/autoconfigure/RateLimiterAutoConfiguration.java index 20511caee7..cafa670d99 100644 --- a/resilience4j-spring-boot/src/main/java/io/github/resilience4j/ratelimiter/autoconfigure/RateLimiterAutoConfiguration.java +++ b/resilience4j-spring-boot/src/main/java/io/github/resilience4j/ratelimiter/autoconfigure/RateLimiterAutoConfiguration.java @@ -21,8 +21,6 @@ import io.github.resilience4j.ratelimiter.event.RateLimiterEvent; import io.github.resilience4j.ratelimiter.monitoring.endpoint.RateLimiterEndpoint; import io.github.resilience4j.ratelimiter.monitoring.endpoint.RateLimiterEventsEndpoint; -import io.github.resilience4j.ratelimiter.monitoring.health.RateLimiterHealthIndicator; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.boot.actuate.autoconfigure.EndpointAutoConfiguration; import org.springframework.boot.actuate.endpoint.Endpoint; import org.springframework.boot.autoconfigure.AutoConfigureBefore; @@ -32,8 +30,6 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -import javax.annotation.PostConstruct; - /** * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration * Auto-configuration} for resilience4j ratelimiter. @@ -44,46 +40,20 @@ @Import(RateLimiterConfigurationOnMissingBean.class) @AutoConfigureBefore(EndpointAutoConfiguration.class) public class RateLimiterAutoConfiguration { - private final RateLimiterProperties rateLimiterProperties; - private final RateLimiterRegistry rateLimiterRegistry; - private final ConfigurableBeanFactory beanFactory; - - public RateLimiterAutoConfiguration(RateLimiterProperties rateLimiterProperties, RateLimiterRegistry rateLimiterRegistry, ConfigurableBeanFactory beanFactory) { - this.rateLimiterProperties = rateLimiterProperties; - this.rateLimiterRegistry = rateLimiterRegistry; - this.beanFactory = beanFactory; - } - @Bean + @Configuration @ConditionalOnClass(value = {Endpoint.class}) - public RateLimiterEndpoint rateLimiterEndpoint(RateLimiterRegistry rateLimiterRegistry) { - return new RateLimiterEndpoint(rateLimiterRegistry); - } + public static class RateLimiterEndpointConfiguration { - @Bean - @ConditionalOnClass(value = {Endpoint.class}) - public RateLimiterEventsEndpoint rateLimiterEventsEndpoint(EventConsumerRegistry eventsConsumerRegistry) { - return new RateLimiterEventsEndpoint(eventsConsumerRegistry); - } + @Bean + public RateLimiterEndpoint rateLimiterEndpoint(RateLimiterRegistry rateLimiterRegistry) { + return new RateLimiterEndpoint(rateLimiterRegistry); + } - @PostConstruct - public void configureHealthIndicators() { - rateLimiterProperties.getInstances().forEach( - (name, properties) -> { - RateLimiter rateLimiter = rateLimiterRegistry.rateLimiter(name); - if (properties.getRegisterHealthIndicator() != null && properties.getRegisterHealthIndicator()) { - createHealthIndicatorForLimiter(beanFactory, name, rateLimiter); - } - } - ); - } + @Bean + public RateLimiterEventsEndpoint rateLimiterEventsEndpoint(EventConsumerRegistry eventsConsumerRegistry) { + return new RateLimiterEventsEndpoint(eventsConsumerRegistry); + } - private void createHealthIndicatorForLimiter(ConfigurableBeanFactory beanFactory, String name, RateLimiter rateLimiter) { - beanFactory.registerSingleton( - name + "RateLimiterHealthIndicator", - new RateLimiterHealthIndicator(rateLimiter) - ); } - - } diff --git a/resilience4j-spring-boot/src/main/java/io/github/resilience4j/ratelimiter/autoconfigure/RateLimitersHealthIndicatorAutoConfiguration.java b/resilience4j-spring-boot/src/main/java/io/github/resilience4j/ratelimiter/autoconfigure/RateLimitersHealthIndicatorAutoConfiguration.java new file mode 100644 index 0000000000..61769549da --- /dev/null +++ b/resilience4j-spring-boot/src/main/java/io/github/resilience4j/ratelimiter/autoconfigure/RateLimitersHealthIndicatorAutoConfiguration.java @@ -0,0 +1,31 @@ +package io.github.resilience4j.ratelimiter.autoconfigure; + +import io.github.resilience4j.ratelimiter.RateLimiter; +import io.github.resilience4j.ratelimiter.RateLimiterRegistry; +import io.github.resilience4j.ratelimiter.configure.RateLimiterConfigurationProperties; +import io.github.resilience4j.ratelimiter.monitoring.health.RateLimitersHealthIndicator; +import org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration; +import org.springframework.boot.actuate.health.HealthAggregator; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConditionalOnClass({RateLimiter.class, HealthIndicator.class}) +@AutoConfigureAfter(RateLimiterAutoConfiguration.class) +@AutoConfigureBefore(HealthIndicatorAutoConfiguration.class) +public class RateLimitersHealthIndicatorAutoConfiguration { + + @Bean + @ConditionalOnMissingBean(name = "rateLimitersHealthIndicator") + public RateLimitersHealthIndicator rateLimitersHealthIndicator(RateLimiterRegistry rateLimiterRegistry, + RateLimiterConfigurationProperties rateLimiterProperties, + HealthAggregator healthAggregator) { + return new RateLimitersHealthIndicator(rateLimiterRegistry, rateLimiterProperties, healthAggregator); + } + +} diff --git a/resilience4j-spring-boot/src/main/java/io/github/resilience4j/retry/autoconfigure/RetryAutoConfiguration.java b/resilience4j-spring-boot/src/main/java/io/github/resilience4j/retry/autoconfigure/RetryAutoConfiguration.java index 2753e6d817..82bb0899b1 100644 --- a/resilience4j-spring-boot/src/main/java/io/github/resilience4j/retry/autoconfigure/RetryAutoConfiguration.java +++ b/resilience4j-spring-boot/src/main/java/io/github/resilience4j/retry/autoconfigure/RetryAutoConfiguration.java @@ -43,16 +43,19 @@ @AutoConfigureBefore(EndpointAutoConfiguration.class) public class RetryAutoConfiguration { - @Bean + @Configuration @ConditionalOnClass(value = {Endpoint.class}) - public RetryEndpoint retryEndpoint(RetryRegistry retryRegistry) { - return new RetryEndpoint(retryRegistry); - } + public static class BulkheadEndpointConfiguration { - @Bean - @ConditionalOnClass(value = {Endpoint.class}) - public RetryEventsEndpoint retryEventsEndpoint(EventConsumerRegistry eventConsumerRegistry) { - return new RetryEventsEndpoint(eventConsumerRegistry); - } + @Bean + public RetryEndpoint retryEndpoint(RetryRegistry retryRegistry) { + return new RetryEndpoint(retryRegistry); + } + @Bean + public RetryEventsEndpoint retryEventsEndpoint(EventConsumerRegistry eventConsumerRegistry) { + return new RetryEventsEndpoint(eventConsumerRegistry); + } + + } } diff --git a/resilience4j-spring-boot/src/main/resources/META-INF/spring.factories b/resilience4j-spring-boot/src/main/resources/META-INF/spring.factories index 8bbeb9d49b..4b364036bb 100644 --- a/resilience4j-spring-boot/src/main/resources/META-INF/spring.factories +++ b/resilience4j-spring-boot/src/main/resources/META-INF/spring.factories @@ -10,7 +10,9 @@ io.github.resilience4j.circuitbreaker.autoconfigure.CircuitBreakerAutoConfigurat io.github.resilience4j.circuitbreaker.autoconfigure.CircuitBreakerMetricsAutoConfiguration,\ io.github.resilience4j.circuitbreaker.autoconfigure.CircuitBreakerMicrometerAutoConfiguration,\ io.github.resilience4j.circuitbreaker.autoconfigure.CircuitBreakerPrometheusAutoConfiguration,\ +io.github.resilience4j.circuitbreaker.autoconfigure.CircuitBreakersHealthIndicatorAutoConfiguration,\ io.github.resilience4j.ratelimiter.autoconfigure.RateLimiterAutoConfiguration,\ io.github.resilience4j.ratelimiter.autoconfigure.RateLimiterMetricsAutoConfiguration,\ io.github.resilience4j.ratelimiter.autoconfigure.RateLimiterMicrometerAutoConfiguration,\ -io.github.resilience4j.ratelimiter.autoconfigure.RateLimiterPrometheusAutoConfiguration +io.github.resilience4j.ratelimiter.autoconfigure.RateLimiterPrometheusAutoConfiguration,\ +io.github.resilience4j.ratelimiter.autoconfigure.RateLimitersHealthIndicatorAutoConfiguration diff --git a/resilience4j-spring-boot/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerAutoConfigurationTest.java b/resilience4j-spring-boot/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerAutoConfigurationTest.java index cee8958dc2..6495ba0da4 100644 --- a/resilience4j-spring-boot/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerAutoConfigurationTest.java +++ b/resilience4j-spring-boot/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerAutoConfigurationTest.java @@ -109,9 +109,9 @@ public void testCircuitBreakerAutoConfiguration() throws IOException { // expect no health indicator for backendB, as it is disabled via properties ResponseEntity healthResponse = restTemplate.getForEntity("/health", String.class); assertThat(healthResponse.getBody()).isNotNull(); - assertThat(healthResponse.getBody()).contains("backendACircuitBreaker"); - assertThat(healthResponse.getBody()).doesNotContain("backendBCircuitBreaker"); - assertThat(healthResponse.getBody()).doesNotContain("dynamicBackend"); + assertThat(healthResponse.getBody()).contains("backendA"); + assertThat(healthResponse.getBody()).doesNotContain("backendB"); + assertThat(healthResponse.getBody()).contains("dynamicBackend"); // Verify that an exception for which setRecordFailurePredicate returns false and it is not included in // setRecordExceptions evaluates to false. diff --git a/resilience4j-spring-boot/src/test/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakerConfigurationOnMissingBeanTest.java b/resilience4j-spring-boot/src/test/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakerConfigurationOnMissingBeanTest.java index 3e7432f31e..8e8806e81f 100644 --- a/resilience4j-spring-boot/src/test/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakerConfigurationOnMissingBeanTest.java +++ b/resilience4j-spring-boot/src/test/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakerConfigurationOnMissingBeanTest.java @@ -26,6 +26,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; @@ -42,6 +43,7 @@ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { + HealthIndicatorAutoConfiguration.class, CircuitBreakerConfigurationOnMissingBeanTest.ConfigWithOverrides.class, CircuitBreakerAutoConfiguration.class, CircuitBreakerConfigurationOnMissingBean.class diff --git a/resilience4j-spring-boot/src/test/java/io/github/resilience4j/ratelimiter/autoconfigure/RateLimiterConfigurationOnMissingBeanTest.java b/resilience4j-spring-boot/src/test/java/io/github/resilience4j/ratelimiter/autoconfigure/RateLimiterConfigurationOnMissingBeanTest.java index 45b4cfadc2..d031eb285f 100644 --- a/resilience4j-spring-boot/src/test/java/io/github/resilience4j/ratelimiter/autoconfigure/RateLimiterConfigurationOnMissingBeanTest.java +++ b/resilience4j-spring-boot/src/test/java/io/github/resilience4j/ratelimiter/autoconfigure/RateLimiterConfigurationOnMissingBeanTest.java @@ -25,6 +25,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.autoconfigure.HealthIndicatorAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; @@ -45,6 +46,7 @@ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { + HealthIndicatorAutoConfiguration.class, RateLimiterConfigurationOnMissingBeanTest.ConfigWithOverrides.class, RateLimiterAutoConfiguration.class, RateLimiterConfigurationOnMissingBean.class diff --git a/resilience4j-spring-boot/src/test/resources/application.yaml b/resilience4j-spring-boot/src/test/resources/application.yaml index b775d2d1d8..9675f590ed 100644 --- a/resilience4j-spring-boot/src/test/resources/application.yaml +++ b/resilience4j-spring-boot/src/test/resources/application.yaml @@ -67,6 +67,7 @@ resilience4j.ratelimiter: limitForPeriod: 100 limitRefreshPeriod: 500ms timeoutDuration: 3s + registerHealthIndicator: false resilience4j.bulkhead: backends: diff --git a/resilience4j-spring-boot2/src/main/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakerConfigurationOnMissingBean.java b/resilience4j-spring-boot2/src/main/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakerConfigurationOnMissingBean.java index 66cf007939..db44ea60d5 100644 --- a/resilience4j-spring-boot2/src/main/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakerConfigurationOnMissingBean.java +++ b/resilience4j-spring-boot2/src/main/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakerConfigurationOnMissingBean.java @@ -15,37 +15,18 @@ */ package io.github.resilience4j.circuitbreaker.autoconfigure; -import io.github.resilience4j.circuitbreaker.CircuitBreaker; import io.github.resilience4j.circuitbreaker.configure.CircuitBreakerConfigurationProperties; import io.github.resilience4j.circuitbreaker.event.CircuitBreakerEvent; -import io.github.resilience4j.circuitbreaker.monitoring.health.CircuitBreakerHealthIndicator; import io.github.resilience4j.consumer.EventConsumerRegistry; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; -import org.springframework.boot.actuate.health.HealthIndicatorRegistry; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import java.util.Map; - @Configuration -public class CircuitBreakerConfigurationOnMissingBean extends AbstractCircuitBreakerConfigurationOnMissingBean implements ApplicationContextAware { - - private final ConfigurableBeanFactory beanFactory; - private ApplicationContext applicationContext; - private HealthIndicatorRegistry healthIndicatorRegistry; +public class CircuitBreakerConfigurationOnMissingBean extends AbstractCircuitBreakerConfigurationOnMissingBean { - public CircuitBreakerConfigurationOnMissingBean(CircuitBreakerConfigurationProperties circuitBreakerProperties, - ConfigurableBeanFactory beanFactory) { + public CircuitBreakerConfigurationOnMissingBean(CircuitBreakerConfigurationProperties circuitBreakerProperties) { super(circuitBreakerProperties); - this.beanFactory = beanFactory; - } - - @Override - public void setApplicationContext(ApplicationContext applicationContext){ - this.applicationContext = applicationContext; } @Bean @@ -54,32 +35,4 @@ public EventConsumerRegistry eventConsumerRegistry() { return circuitBreakerConfiguration.eventConsumerRegistry(); } - @Override - protected void createHealthIndicatorForCircuitBreaker(CircuitBreaker circuitBreaker, CircuitBreakerConfigurationProperties circuitBreakerProperties) { - boolean registerHealthIndicator = circuitBreakerProperties.findCircuitBreakerProperties(circuitBreaker.getName()) - .map(io.github.resilience4j.common.circuitbreaker.configuration.CircuitBreakerConfigurationProperties.InstanceProperties::getRegisterHealthIndicator) - .orElse(true); - - if (registerHealthIndicator) { - CircuitBreakerHealthIndicator healthIndicator = new CircuitBreakerHealthIndicator(circuitBreaker); - String circuitBreakerName = circuitBreaker.getName() + "CircuitBreaker"; - beanFactory.registerSingleton( - circuitBreakerName + "HealthIndicator", - healthIndicator - ); - // To support health indicators created after the health registry was created, look up to see if it's in - // the application context. If it is, save it off so we don't need to search for it again, then register - // the new health indicator with the registry. - if (applicationContext != null && healthIndicatorRegistry == null) { - Map healthRegistryBeans = applicationContext.getBeansOfType(HealthIndicatorRegistry.class); - if (healthRegistryBeans.size() > 0) { - healthIndicatorRegistry = healthRegistryBeans.values().iterator().next(); - } - } - - if (healthIndicatorRegistry != null && healthIndicatorRegistry.get(circuitBreakerName) == null) { - healthIndicatorRegistry.register(circuitBreakerName, healthIndicator); - } - } - } } diff --git a/resilience4j-spring-boot2/src/main/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakersHealthIndicatorAutoConfiguration.java b/resilience4j-spring-boot2/src/main/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakersHealthIndicatorAutoConfiguration.java new file mode 100644 index 0000000000..a8f986bcbe --- /dev/null +++ b/resilience4j-spring-boot2/src/main/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakersHealthIndicatorAutoConfiguration.java @@ -0,0 +1,31 @@ +package io.github.resilience4j.circuitbreaker.autoconfigure; + +import io.github.resilience4j.circuitbreaker.CircuitBreaker; +import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry; +import io.github.resilience4j.circuitbreaker.configure.CircuitBreakerConfigurationProperties; +import io.github.resilience4j.circuitbreaker.monitoring.health.CircuitBreakersHealthIndicator; +import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration; +import org.springframework.boot.actuate.health.HealthAggregator; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConditionalOnClass({CircuitBreaker.class, HealthIndicator.class}) +@AutoConfigureAfter(CircuitBreakerAutoConfiguration.class) +@AutoConfigureBefore(HealthIndicatorAutoConfiguration.class) +public class CircuitBreakersHealthIndicatorAutoConfiguration { + + @Bean + @ConditionalOnMissingBean(name = "circuitBreakersHealthIndicator") + public CircuitBreakersHealthIndicator circuitBreakersHealthIndicator(CircuitBreakerRegistry circuitBreakerRegistry, + CircuitBreakerConfigurationProperties circuitBreakerProperties, + HealthAggregator healthAggregator) { + return new CircuitBreakersHealthIndicator(circuitBreakerRegistry, circuitBreakerProperties, healthAggregator); + } + +} diff --git a/resilience4j-spring-boot2/src/main/java/io/github/resilience4j/ratelimiter/autoconfigure/RateLimiterAutoConfiguration.java b/resilience4j-spring-boot2/src/main/java/io/github/resilience4j/ratelimiter/autoconfigure/RateLimiterAutoConfiguration.java index 38443253a2..8f97a03967 100644 --- a/resilience4j-spring-boot2/src/main/java/io/github/resilience4j/ratelimiter/autoconfigure/RateLimiterAutoConfiguration.java +++ b/resilience4j-spring-boot2/src/main/java/io/github/resilience4j/ratelimiter/autoconfigure/RateLimiterAutoConfiguration.java @@ -22,8 +22,6 @@ import io.github.resilience4j.ratelimiter.event.RateLimiterEvent; import io.github.resilience4j.ratelimiter.monitoring.endpoint.RateLimiterEndpoint; import io.github.resilience4j.ratelimiter.monitoring.endpoint.RateLimiterEventsEndpoint; -import io.github.resilience4j.ratelimiter.monitoring.health.RateLimiterHealthIndicator; -import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration; import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint; import org.springframework.boot.actuate.endpoint.annotation.Endpoint; @@ -34,8 +32,6 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -import javax.annotation.PostConstruct; - /** * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration * Auto-configuration} for resilience4j ratelimiter. @@ -46,15 +42,6 @@ @Import({RateLimiterConfigurationOnMissingBean.class, FallbackConfigurationOnMissingBean.class}) @AutoConfigureBefore(EndpointAutoConfiguration.class) public class RateLimiterAutoConfiguration { - private final RateLimiterProperties rateLimiterProperties; - private final RateLimiterRegistry rateLimiterRegistry; - private final ConfigurableBeanFactory beanFactory; - - public RateLimiterAutoConfiguration(RateLimiterProperties rateLimiterProperties, RateLimiterRegistry rateLimiterRegistry, ConfigurableBeanFactory beanFactory) { - this.rateLimiterProperties = rateLimiterProperties; - this.rateLimiterRegistry = rateLimiterRegistry; - this.beanFactory = beanFactory; - } @Bean @ConditionalOnEnabledEndpoint @@ -70,22 +57,4 @@ public RateLimiterEventsEndpoint rateLimiterEventsEndpoint(EventConsumerRegistry return new RateLimiterEventsEndpoint(eventConsumerRegistry); } - @PostConstruct - public void configureHealthIndicators() { - rateLimiterProperties.getInstances().forEach( - (name, properties) -> { - if (properties.getRegisterHealthIndicator() != null && properties.getRegisterHealthIndicator()) { - createHealthIndicatorForLimiter(name); - } - } - ); - } - - private void createHealthIndicatorForLimiter(String name) { - RateLimiter rateLimiter = rateLimiterRegistry.rateLimiter(name); - beanFactory.registerSingleton( - name + "RateLimiterHealthIndicator", - new RateLimiterHealthIndicator(rateLimiter) - ); - } } diff --git a/resilience4j-spring-boot2/src/main/java/io/github/resilience4j/ratelimiter/autoconfigure/RateLimitersHealthIndicatorAutoConfiguration.java b/resilience4j-spring-boot2/src/main/java/io/github/resilience4j/ratelimiter/autoconfigure/RateLimitersHealthIndicatorAutoConfiguration.java new file mode 100644 index 0000000000..03ba0ff224 --- /dev/null +++ b/resilience4j-spring-boot2/src/main/java/io/github/resilience4j/ratelimiter/autoconfigure/RateLimitersHealthIndicatorAutoConfiguration.java @@ -0,0 +1,31 @@ +package io.github.resilience4j.ratelimiter.autoconfigure; + +import io.github.resilience4j.ratelimiter.RateLimiter; +import io.github.resilience4j.ratelimiter.RateLimiterRegistry; +import io.github.resilience4j.ratelimiter.configure.RateLimiterConfigurationProperties; +import io.github.resilience4j.ratelimiter.monitoring.health.RateLimitersHealthIndicator; +import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration; +import org.springframework.boot.actuate.health.HealthAggregator; +import org.springframework.boot.actuate.health.HealthIndicator; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.AutoConfigureBefore; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConditionalOnClass({RateLimiter.class, HealthIndicator.class}) +@AutoConfigureAfter(RateLimiterAutoConfiguration.class) +@AutoConfigureBefore(HealthIndicatorAutoConfiguration.class) +public class RateLimitersHealthIndicatorAutoConfiguration { + + @Bean + @ConditionalOnMissingBean(name = "rateLimitersHealthIndicator") + public RateLimitersHealthIndicator rateLimitersHealthIndicator(RateLimiterRegistry rateLimiterRegistry, + RateLimiterConfigurationProperties rateLimiterProperties, + HealthAggregator healthAggregator) { + return new RateLimitersHealthIndicator(rateLimiterRegistry, rateLimiterProperties, healthAggregator); + } + +} diff --git a/resilience4j-spring-boot2/src/main/resources/META-INF/spring.factories b/resilience4j-spring-boot2/src/main/resources/META-INF/spring.factories index 22b5600c7c..cab327cf2f 100644 --- a/resilience4j-spring-boot2/src/main/resources/META-INF/spring.factories +++ b/resilience4j-spring-boot2/src/main/resources/META-INF/spring.factories @@ -5,5 +5,7 @@ io.github.resilience4j.retry.autoconfigure.RetryAutoConfiguration,\ io.github.resilience4j.retry.autoconfigure.RetryMetricsAutoConfiguration,\ io.github.resilience4j.circuitbreaker.autoconfigure.CircuitBreakerAutoConfiguration,\ io.github.resilience4j.circuitbreaker.autoconfigure.CircuitBreakerMetricsAutoConfiguration,\ +io.github.resilience4j.circuitbreaker.autoconfigure.CircuitBreakersHealthIndicatorAutoConfiguration,\ io.github.resilience4j.ratelimiter.autoconfigure.RateLimiterAutoConfiguration,\ -io.github.resilience4j.ratelimiter.autoconfigure.RateLimiterMetricsAutoConfiguration +io.github.resilience4j.ratelimiter.autoconfigure.RateLimiterMetricsAutoConfiguration,\ +io.github.resilience4j.ratelimiter.autoconfigure.RateLimitersHealthIndicatorAutoConfiguration diff --git a/resilience4j-spring-boot2/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerAutoConfigurationRxJava2Test.java b/resilience4j-spring-boot2/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerAutoConfigurationRxJava2Test.java index f2cd181efd..489ca0f370 100644 --- a/resilience4j-spring-boot2/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerAutoConfigurationRxJava2Test.java +++ b/resilience4j-spring-boot2/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerAutoConfigurationRxJava2Test.java @@ -97,10 +97,11 @@ public void testCircuitBreakerAutoConfigurationReactiveRxJava2() throws IOExcept assertThat(circuitBreakerEventList.getBody().getCircuitBreakerEvents()).hasSize(2); // expect no health indicator for backendB, as it is disabled via properties - ResponseEntity healthResponse = restTemplate.getForEntity("/actuator/health", HealthResponse.class); + ResponseEntity healthResponse = restTemplate.getForEntity("/actuator/health", CompositeHealthResponse.class); assertThat(healthResponse.getBody().getDetails()).isNotNull(); - assertThat(healthResponse.getBody().getDetails().get("backendACircuitBreaker")).isNotNull(); - assertThat(healthResponse.getBody().getDetails().get("backendBCircuitBreaker")).isNull(); + assertThat(healthResponse.getBody().getDetails().get("circuitBreakers")).isNotNull(); + assertThat(healthResponse.getBody().getDetails().get("circuitBreakers").getDetails().get("backendA")).isNotNull(); + assertThat(healthResponse.getBody().getDetails().get("circuitBreakers").getDetails().get("backendB")).isNull(); // Observable test try { @@ -157,8 +158,19 @@ public void testCircuitBreakerAutoConfigurationReactiveRxJava2() throws IOExcept } + private static final class CompositeHealthResponse { + private Map details; - private final static class HealthResponse { + public Map getDetails() { + return details; + } + + public void setDetails(Map details) { + this.details = details; + } + } + + private static final class HealthResponse { private Map details; public Map getDetails() { diff --git a/resilience4j-spring-boot2/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerAutoConfigurationTest.java b/resilience4j-spring-boot2/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerAutoConfigurationTest.java index 39e2cae527..7d9dcbb42d 100644 --- a/resilience4j-spring-boot2/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerAutoConfigurationTest.java +++ b/resilience4j-spring-boot2/src/test/java/io/github/resilience4j/circuitbreaker/CircuitBreakerAutoConfigurationTest.java @@ -25,6 +25,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.health.Health; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.http.ResponseEntity; @@ -108,13 +109,15 @@ public void testCircuitBreakerAutoConfiguration() throws IOException { assertThat(circuitBreakerEventList.getBody().getCircuitBreakerEvents()).hasSize(2); // expect no health indicator for backendB, as it is disabled via properties - ResponseEntity healthResponse = restTemplate.getForEntity("/actuator/health", HealthResponse.class); + ResponseEntity healthResponse = restTemplate.getForEntity("/actuator/health", CompositeHealthResponse.class); assertThat(healthResponse.getBody().getDetails()).isNotNull(); - assertThat(healthResponse.getBody().getDetails().get("backendACircuitBreaker")).isNotNull(); - assertThat(healthResponse.getBody().getDetails().get("backendBCircuitBreaker")).isNull(); - assertThat(healthResponse.getBody().getDetails().get("backendSharedACircuitBreaker")).isNotNull(); - assertThat(healthResponse.getBody().getDetails().get("backendSharedBCircuitBreaker")).isNotNull(); - assertThat(healthResponse.getBody().getDetails().get("dynamicBackend")).isNull(); + assertThat(healthResponse.getBody().getDetails().get("circuitBreakers")).isNotNull(); + HealthResponse circuitBreakerHealth = healthResponse.getBody().getDetails().get("circuitBreakers"); + assertThat(circuitBreakerHealth.getDetails().get("backendA")).isNotNull(); + assertThat(circuitBreakerHealth.getDetails().get("backendB")).isNull(); + assertThat(circuitBreakerHealth.getDetails().get("backendSharedA")).isNotNull(); + assertThat(circuitBreakerHealth.getDetails().get("backendSharedB")).isNotNull(); + assertThat(circuitBreakerHealth.getDetails().get("dynamicBackend")).isNotNull(); assertThat(circuitBreaker.getCircuitBreakerConfig().getRecordExceptionPredicate().test(new RecordedException())).isTrue(); assertThat(circuitBreaker.getCircuitBreakerConfig().getIgnoreExceptionPredicate().test(new IgnoredException())).isTrue(); @@ -193,12 +196,14 @@ public void testCircuitBreakerAutoConfigurationAsync() throws IOException, Execu assertThat(circuitBreakerEventList.getBody().getCircuitBreakerEvents()).hasSize(2); // expect no health indicator for backendB, as it is disabled via properties - ResponseEntity healthResponse = restTemplate.getForEntity("/actuator/health", HealthResponse.class); + ResponseEntity healthResponse = restTemplate.getForEntity("/actuator/health", CompositeHealthResponse.class); assertThat(healthResponse.getBody().getDetails()).isNotNull(); - assertThat(healthResponse.getBody().getDetails().get("backendACircuitBreaker")).isNotNull(); - assertThat(healthResponse.getBody().getDetails().get("backendBCircuitBreaker")).isNull(); - assertThat(healthResponse.getBody().getDetails().get("backendSharedACircuitBreaker")).isNotNull(); - assertThat(healthResponse.getBody().getDetails().get("backendSharedBCircuitBreaker")).isNotNull(); + assertThat(healthResponse.getBody().getDetails().get("circuitBreakers")).isNotNull(); + HealthResponse circuitBreakerHealth = healthResponse.getBody().getDetails().get("circuitBreakers"); + assertThat(circuitBreakerHealth.getDetails().get("backendA")).isNotNull(); + assertThat(circuitBreakerHealth.getDetails().get("backendB")).isNull(); + assertThat(circuitBreakerHealth.getDetails().get("backendSharedA")).isNotNull(); + assertThat(circuitBreakerHealth.getDetails().get("backendSharedB")).isNotNull(); } @@ -243,10 +248,12 @@ public void testCircuitBreakerAutoConfigurationReactive() throws IOException { assertThat(circuitBreakerEventList.getBody().getCircuitBreakerEvents()).hasSize(2); // expect no health indicator for backendB, as it is disabled via properties - ResponseEntity healthResponse = restTemplate.getForEntity("/actuator/health", HealthResponse.class); + ResponseEntity healthResponse = restTemplate.getForEntity("/actuator/health", CompositeHealthResponse.class); assertThat(healthResponse.getBody().getDetails()).isNotNull(); - assertThat(healthResponse.getBody().getDetails().get("backendACircuitBreaker")).isNotNull(); - assertThat(healthResponse.getBody().getDetails().get("backendBCircuitBreaker")).isNull(); + assertThat(healthResponse.getBody().getDetails().get("circuitBreakers")).isNotNull(); + HealthResponse circuitBreakerHealth = healthResponse.getBody().getDetails().get("circuitBreakers"); + assertThat(circuitBreakerHealth.getDetails().get("backendA")).isNotNull(); + assertThat(circuitBreakerHealth.getDetails().get("backendB")).isNull(); assertThat(circuitBreakerAspect.getOrder()).isEqualTo(400); assertThat(circuitBreaker.getMetrics().getNumberOfBufferedCalls()).isEqualTo(2); @@ -254,7 +261,19 @@ public void testCircuitBreakerAutoConfigurationReactive() throws IOException { assertThat(circuitBreaker.getMetrics().getNumberOfFailedCalls()).isEqualTo(1); } - private final static class HealthResponse { + private static final class CompositeHealthResponse { + private Map details; + + public Map getDetails() { + return details; + } + + public void setDetails(Map details) { + this.details = details; + } + } + + private static final class HealthResponse { private Map details; public Map getDetails() { diff --git a/resilience4j-spring-boot2/src/test/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakerConfigurationOnMissingBeanTest.java b/resilience4j-spring-boot2/src/test/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakerConfigurationOnMissingBeanTest.java index 985653f7d6..adf5ff5116 100644 --- a/resilience4j-spring-boot2/src/test/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakerConfigurationOnMissingBeanTest.java +++ b/resilience4j-spring-boot2/src/test/java/io/github/resilience4j/circuitbreaker/autoconfigure/CircuitBreakerConfigurationOnMissingBeanTest.java @@ -7,6 +7,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -25,6 +26,7 @@ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { + HealthIndicatorAutoConfiguration.class, CircuitBreakerConfigurationOnMissingBeanTest.ConfigWithOverrides.class, CircuitBreakerAutoConfiguration.class, CircuitBreakerConfigurationOnMissingBean.class diff --git a/resilience4j-spring-boot2/src/test/java/io/github/resilience4j/ratelimiter/autoconfigure/RateLimiterConfigurationOnMissingBeanTest.java b/resilience4j-spring-boot2/src/test/java/io/github/resilience4j/ratelimiter/autoconfigure/RateLimiterConfigurationOnMissingBeanTest.java index a42b2d8a8d..3f72313348 100644 --- a/resilience4j-spring-boot2/src/test/java/io/github/resilience4j/ratelimiter/autoconfigure/RateLimiterConfigurationOnMissingBeanTest.java +++ b/resilience4j-spring-boot2/src/test/java/io/github/resilience4j/ratelimiter/autoconfigure/RateLimiterConfigurationOnMissingBeanTest.java @@ -7,6 +7,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -27,6 +28,7 @@ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { + HealthIndicatorAutoConfiguration.class, RateLimiterConfigurationOnMissingBeanTest.ConfigWithOverrides.class, RateLimiterAutoConfiguration.class, RateLimiterConfigurationOnMissingBean.class diff --git a/resilience4j-timelimiter/src/test/java/io/github/resilience4j/timelimiter/TimeLimiterEventPublisherTest.java b/resilience4j-timelimiter/src/test/java/io/github/resilience4j/timelimiter/TimeLimiterEventPublisherTest.java index 5bac081f9b..44b2f63dc0 100644 --- a/resilience4j-timelimiter/src/test/java/io/github/resilience4j/timelimiter/TimeLimiterEventPublisherTest.java +++ b/resilience4j-timelimiter/src/test/java/io/github/resilience4j/timelimiter/TimeLimiterEventPublisherTest.java @@ -71,7 +71,7 @@ public void shouldConsumeOnTimeoutEvent() { timeLimiter.getEventPublisher() .onTimeout(event -> logger.info(event.getEventType().toString())); Supplier> futureSupplier = () -> - CompletableFuture.supplyAsync(this::fail); + CompletableFuture.supplyAsync(this::timeout); Try.ofCallable(timeLimiter.decorateFutureSupplier(futureSupplier)); @@ -95,4 +95,12 @@ private String fail() { throw new RuntimeException(); } + private String timeout() { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // nothing + } + return "timeout"; + } } \ No newline at end of file