HomeGuidesChangelog
GuidesGitHubLog In
Guides

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()