Intro to Resilience4j

I would like to dedicate the first story in my tech blog to talking about an interesting library Resilience4j which allow you to deal really easily with fault tolerance problems.

Library has a few dependencies and provides you with a lot of components and patterns, which you may use to tackle problems with communication between services in your architecture.

  • Circuit breaking
  • Rate limiting
  • Retrying
  • Caching

Library has a lot of parameters to configure, which allows you to have a great control over integrations in your services. For example for circuit breaker you could configure failure threshold, duration in open state and sizes of buffer in closed or half-open state.

// Create a custom configuration for a CircuitBreaker
CircuitBreakerConfig circuitBreakerConfig = CircuitBreakerConfig.custom()

// Create a CircuitBreakerRegistry with a custom global configuration
CircuitBreakerRegistry circuitBreakerRegistry = CircuitBreakerRegistry.of(circuitBreakerConfig);

// Get a CircuitBreaker from the CircuitBreakerRegistry with the global default configuration
CircuitBreaker circuitBreaker2 = circuitBreakerRegistry.circuitBreaker("otherName");

// Get a CircuitBreaker from the CircuitBreakerRegistry with a custom configuration
CircuitBreaker circuitBreaker = circuitBreakerRegistry.circuitBreaker("uniqueName", circuitBreakerConfig);

There are all sort of high order decorator functions which allow you to use features of this library in a functional style. It’s really easy to chain functions or recover after failure using a monad Try.

// Given
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("testName");
CircuitBreaker anotherCircuitBreaker = CircuitBreaker.ofDefaults("anotherTestName");

// When I create a Supplier and a Function which are decorated by different CircuitBreakers
Try.CheckedSupplier<String> decoratedSupplier = CircuitBreaker
.decorateCheckedSupplier(circuitBreaker, () -> "Hello");

Try.CheckedFunction<String, String> decoratedFunction = CircuitBreaker
.decorateCheckedFunction(anotherCircuitBreaker, (input) -> input + " world");

// and I chain a function with map
Try<String> result = Try.of(decoratedSupplier)

// Then
assertThat(result.get()).isEqualTo("Hello world");

All the components have interface to get operational metrics and you could easily use them to monitor state of your system. Also there are integrations with popular libraries for monitoring, e.g. dropwizard.

CircuitBreaker.Metrics metrics = circuitBreaker.getMetrics();
// Returns the failure rate in percentage.
float failureRate = metrics.getFailureRate();
// Returns the current number of buffered calls.
int bufferedCalls = metrics.getNumberOfBufferedCalls();
// Returns the current number of failed calls.
int failedCalls = metrics.getNumberOfFailedCalls();