Examples
Examples of resilience4j-circuitbreaker
Create a CircuitBreakerRegistry
Create a CircuitBreakerRegistry with a custom CircuitBreakerConfig.
// Create a custom configuration for a CircuitBreaker
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()
.failureRateThreshold(50)
.waitDurationInOpenState(Duration.ofMillis(1000))
.permittedNumberOfCallsInHalfOpenState(2)
.slidingWindowSize(2)
.recordExceptions(IOException.class, TimeoutException.class)
.ignoreExceptions(BusinessException.class, OtherBusinessException.class)
.build();
// Create a CircuitBreakerRegistry with a custom global configuration
CircuitBreakerRegistry circuitBreakerRegistry =
CircuitBreakerRegistry.of(circuitBreakerConfig);
// Create a custom configuration for a CircuitBreaker
val circuitBreakerConfig = CircuitBreakerConfig.custom()
.failureRateThreshold(50f)
.waitDurationInOpenState(Duration.ofMillis(1000))
.permittedNumberOfCallsInHalfOpenState(2)
.slidingWindowSize(2)
.recordExceptions(IOException::class.java, TimeoutException::class.java)
.ignoreExceptions(BusinessException::class.java, OtherBusinessException::class.java)
.build()
// Create a CircuitBreakerRegistry with a custom global configuration
val circuitBreakerRegistry = CircuitBreakerRegistry.of(circuitBreakerConfig)
Create a CircuitBreaker
Get a CircuitBreaker from the CircuitBreakerRegistry with the global default configuration
CircuitBreaker circuitBreaker = circuitBreakerRegistry
.circuitBreaker("name");
val circuitBreaker = circuitBreakerRegistry.circuitBreaker("name")
Decorate a functional interface
Decorate your call to BackendService.doSomething()
with a CircuitBreaker and execute the decorated supplier and recover from any exception.
Supplier<String> decoratedSupplier = CircuitBreaker
.decorateSupplier(circuitBreaker, backendService::doSomething);
String result = Try.ofSupplier(decoratedSupplier)
.recover(throwable -> "Hello from Recovery").get();
val decoratedSupplier = CircuitBreaker.decorateSupplier(circuitBreaker, backendService::doSomething)
val result = runCatching { decoratedSupplier.get() }
.recover { "Hello from Recovery" }
.getOrThrow()
Execute a decorated functional interface
When you don't want to decorate your lambda expression, but just execute it and protect the call by a CircuitBreaker.
String result = circuitBreaker
.executeSupplier(backendService::doSomething);
val result: String = circuitBreaker.executeSupplier(backendService::doSomething)
Recover from an exception
If you want to recover from an exception after the CircuitBreaker recorded it as a failure, you can chain the method Vavr´s Try.recover()
. The recovery method is only invoked, if Try.ofSupplier()
returns a Failure<Throwable>
Monad.
When using Kotlin, you can achieve the same behavior by using Kotlin's runCatching
. The recovery logic can be chained using recover
, which is invoked only when an exception occurs, similar to Vavr's Try.recover()
.
// Given
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("testName");
// When I decorate my function and invoke the decorated function
Supplier<String> checkedSupplier =
CircuitBreaker.decorateSupplier(circuitBreaker, () -> {
throw new RuntimeException("BAM!");
});
Try<String> result = Try.ofSupplier(checkedSupplier)
.recover(throwable -> "Hello Recovery");
// Then the function should be a success,
// because the exception could be recovered
assertThat(result.isSuccess()).isTrue();
// and the result must match the result of the recovery function.
assertThat(result.get()).isEqualTo("Hello Recovery");
// Given
val circuitBreaker = CircuitBreaker.ofDefaults("testName")
// When I decorate my function and invoke the decorated function
val checkedSupplier = CircuitBreaker.decorateSupplier(circuitBreaker) {
throw RuntimeException("BAM!")
}
val result = runCatching { checkedSupplier.get() }
.recover { "Hello Recovery" }
// Then the function should be a success,
// because the exception could be recovered
assert(result.isSuccess)
// and the result must match the result of the recovery function.
assert(result.getOrNull() == "Hello Recovery")
If you want to recover from an exception before the CircuitBreaker records it as a failure, you can do the following:
Supplier<String> supplier = () -> {
throw new RuntimeException("BAM!");
};
Supplier<String> supplierWithRecovery = SupplierUtils
.recover(supplier, (exception) -> "Hello Recovery");
String result = circuitBreaker.executeSupplier(supplierWithRecovery);
assertThat(result).isEqualTo("Hello Recovery");
val supplier = Supplier<String> {
throw RuntimeException("BAM!")
}
val supplierWithRecovery = SupplierUtils.recover(supplier) { exception ->
"Hello Recovery"
}
val result = circuitBreaker.executeSupplier(supplierWithRecovery)
assert(result == "Hello Recovery")
SupplierUtils
and CallableUtils
contain other methods like andThen
which can take can be used to chain functions. For example to check the status code of a HTTP response, so that exceptions can be thrown.
Supplier<String> supplierWithResultAndExceptionHandler = SupplierUtils
.andThen(supplier, (result, exception) -> "Hello Recovery");
Supplier<HttpResponse> supplier = () -> httpClient.doRemoteCall();
Supplier<HttpResponse> supplierWithResultHandling = SupplierUtils.andThen(supplier, result -> {
if (result.getStatusCode() == 400) {
throw new ClientException();
} else if (result.getStatusCode() == 500) {
throw new ServerException();
}
return result;
});
HttpResponse httpResponse = circuitBreaker
.executeSupplier(supplierWithResultHandling);
val supplierWithResultAndExceptionHandler = SupplierUtils.andThen(supplier) { result, exception ->
"Hello Recovery"
}
val supplier: Supplier<HttpResponse> = Supplier {
httpClient.doRemoteCall()
}
val supplierWithResultHandling = SupplierUtils.andThen(supplier) { result ->
when (result.statusCode) {
400 -> throw ClientException()
500 -> throw ServerException()
else -> result
}
}
val httpResponse = circuitBreaker.executeSupplier(supplierWithResultHandling)
Reset CircuitBreaker
The Circuit Breaker supports resetting to its original state, losing all the metrics and effectively resetting its Sliding Window.
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("testName");
circuitBreaker.reset();
val circuitBreaker = CircuitBreaker.ofDefaults("testName")
circuitBreaker.reset()
Transition to states manually
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("testName");
circuitBreaker.transitionToDisabledState();
// circuitBreaker.onFailure(...) won't trigger a state change
circuitBreaker.transitionToClosedState(); // will transition to CLOSED state and re-enable normal behaviour, keeping metrics
circuitBreaker.transitionToForcedOpenState();
// circuitBreaker.onSuccess(...) won't trigger a state change
circuitBreaker.reset(); // will transition to CLOSED state and re-enable normal behaviour, losing metrics
val circuitBreaker = CircuitBreaker.ofDefaults("testName")
circuitBreaker.transitionToDisabledState()
// circuitBreaker.onFailure(...) won't trigger a state change
circuitBreaker.transitionToClosedState() // will transition to CLOSED state and re-enable normal behaviour, keeping metrics
circuitBreaker.transitionToForcedOpenState()
// circuitBreaker.onSuccess(...) won't trigger a state change
circuitBreaker.reset() // will transition to CLOSED state and re-enable normal behaviour, losing metrics
Override the RegistryStore
You can override the im-memory RegistryStore by a custom implementation. For example, if you want to use a Cache which removes unused instances after a certain period of time.
CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.custom()
.withRegistryStore(new CacheCircuitBreakerRegistryStore())
.build();
val circuitBreakerRegistry = CircuitBreakerRegistry.custom()
.withRegistryStore(CacheCircuitBreakerRegistryStore())
.build()
Updated about 1 month ago