resilience4j

Resilience4j is a fault tolerance library for Java™

Resilience4j is a lightweight fault tolerance library inspired by Netflix Hystrix, but designed for functional programming.
Resilience4j provides higher-order functions (decorators) to enhance any functional interface, lambda expression or method reference with a Circuit Breaker, Rate Limiter, Retry or Bulkhead. You can stack more than one decorator on any functional interface, lambda expression or method reference. The advantage is that you have the choice to select the decorators you need and nothing else.

Supplier<String> supplier = () -> service.sayHelloWorld(param1);

String result = Decorators.ofSupplier(supplier)
  .withBulkhead(Bulkhead.ofDefaults("name"))
  .withCircuitBreaker(CircuitBreaker.ofDefaults("name"))
  .withRetry(Retry.ofDefaults("name"))
  .withFallback(asList(CallNotPermittedException.class, BulkheadFullException.class),  
      throwable -> "Hello from fallback")
  .get()

With Resilience4j you don’t have to go all-in, you can pick what you need.

Get Started

Getting Started

Getting started with resilience4j-micronaut

Setup

Add the Micronaut Starter of Resilience4j to your compile dependency.

include the maven bom for micronaut dependencies io.micronaut:micronaut-bom:2.0.0.M3.

repositories {
    mavenCentral()
    jCenter()
}

dependencyManagement {
    imports {
        mavenBom 'io.micronaut:micronaut-bom:2.0.0.M3'
    }
}

dependencies {
  annotationProcessor "io.micronaut:micronaut-inject-java"
  annotationProcessor "io.micronaut:micronaut-validation"
  compile "io.github.resilience4j:resilience4j-micronaut:${resilience4jVersion}"
}

Demo

Setup and usage in Micronaut is demonstrated into a demo.

Configuration

You can configure your CircuitBreaker, Retry, RateLimiter, Bulkhead, Thread pool bulkhead and TimeLimiter instances in Micronaut’s application.yml config file.
For example

resilience4j:
  circuitbreaker:
    enabled: true
    instances:
      backendA:
        baseConfig: default
      backendB:
        registerHealthIndicator: true
        slidingWindowSize: 10
        minimumNumberOfCalls: 10
        permittedNumberOfCallsInHalfOpenState: 3
        waitDurationInOpenState: PT5s
        failureRateThreshold: 50
        eventConsumerBufferSize: 10
        recordFailurePredicate: resilience4j.micronaut.demo.exception.RecordFailurePredicate
    configs:
      default:
        registerHealthIndicator: true
        slidingWindowSize: 10
        minimumNumberOfCalls: 5
        permittedNumberOfCallsInHalfOpenState: 3
        automaticTransitionFromOpenToHalfOpenEnabled: true
        waitDurationInOpenState: PT5s
        failureRateThreshold: 50
        eventConsumerBufferSize: 10
        recordExceptions:
          - io.micronaut.http.exceptions.HttpStatusException
          - java.util.concurrent.TimeoutException
          - java.io.IOException
        ignoreExceptions:
          - resilience4j.micronaut.demo.exception.BusinessException
      shared:
        slidingWindowSize: 100
        permittedNumberOfCallsInHalfOpenState: 30
        waitDurationInOpenState: PT1s
        failureRateThreshold: 50
        eventConsumerBufferSize: 10
        ignoreExceptions:
          - resilience4j.micronaut.demo.exception.BusinessException
  retry:
    enabled: true
    configs:
      default:
        maxAttempts: 3
        waitDuration: 100
        retryExceptions:
          - io.micronaut.http.exceptions.HttpStatusException
          - java.util.concurrent.TimeoutException
          - java.io.IOException
        ignoreExceptions:
          - resilience4j.micronaut.demo.exception.BusinessException
    instances:
      backendA:
        baseConfig: default
      backendB:
        baseConfig: default
  bulkhead:
    enabled: true
    configs:
      default:
        maxConcurrentCalls: 100
    instances:
      backendA:
        maxConcurrentCalls: 10
      backendB:
        maxWaitDuration: PT0.01S
        maxConcurrentCalls: 20
  thread-pool-bulkhead:
    enabled: true
    configs:
      default:
        maxThreadPoolSize: 4
        coreThreadPoolSize: 2
        queueCapacity: 2
    instances:
      backendA:
        baseConfig: default
      backendB:
        maxThreadPoolSize: 1
        coreThreadPoolSize: 1
        queueCapacity: 1
  ratelimiter:
    enabled: true
    configs:
      default:
        registerHealthIndicator: false
        limitForPeriod: 10
        limitRefreshPeriod: 1s
        timeoutDuration: 0
        eventConsumerBufferSize: 100
    instances:
      backendA:
        baseConfig: default
      backendB:
        limitForPeriod: 6
        limitRefreshPeriod: PT0.5S
        timeoutDuration: 3s
  timelimiter:
    enabled: true
    configs:
      default:
        cancelRunningFuture: false
        timeoutDuration: PT2s
    instances:
      backendA:
        baseConfig: default
      backendB:
        baseConfig: default

You can also override the default configuration, define shared configurations and overwrite them in Micronaut’s application.yml config file.
For example:

resilience4j: 
  circuitbreaker: 
    enabled: true
  configs: 
    default: 
      eventConsumerBufferSize: 10
      failureRateThreshold: 60
      permittedNumberOfCallsInHalfOpenState: 10
      registerHealthIndicator: true
      slidingWindowSize: 100
      waitDurationInOpenState: 10000
    someShared: 
      permittedNumberOfCallsInHalfOpenState: 10
      slidingWindowSize: 50
  instances: 
    backendA: 
      baseConfig: default
      waitDurationInOpenState: 5000
    backendB: 
     baseConfig: someShared

You can also override the configuration of a specific CircuitBreaker, Bulkhead, Retry, RateLimiter or TimeLimiter instances by using Customizer for specific instance names. The following shows an example of how to override a configured CircuitBreaker backendA in the above in the YAML file:

@Bean
@CircuitBreakerQualifier
public CircuitBreakerConfigCustomizer testCustomizer() {
    return CircuitBreakerConfigCustomizer
        .of("backendA", builder -> builder.slidingWindowSize(100));
}

Resilience4j has its own customizer types which can be used as shown above. The beans have to be marked with the correct Qualifier:

Resilienc4j Type

Instance Customizer class

Qualifier

Circuit breaker

CircuitBreakerConfigCustomizer

@CircuitBreakerQualifier

Retry

RetryConfigCustomizer

@RetryQualifier

Rate limiter

RateLimiterConfigCustomizer

@RateLimiterQualifier

Bulkhead

BulkheadConfigCustomizer

@BulkheadQualifier

ThreadPoolBulkhead

ThreadPoolBulkheadConfigCustomizer

@ThreadPoolBulkheadQualifier

Time Limiter

TimeLimiterConfigCustomizer

@TimeLimiterQualifier

Configuration through Interceptor

beans can be intercepted before they are injected. so additional customization can be issued through intercepting the Resilience4j properties.

package io.micronaut.ignite.docs.config;

@Singleton
public class BulkheadConfiguration implements BeanCreatedEventListener<BulkheadProperties> {
    @Override
    public IgniteConfiguration onCreated(BeanCreatedEvent<BulkheadConfigurationProperties> event) {
        CircuitBreakerProperties configuration = event.getBean();
        return configuration;
    }
}

Resilience4j Type

BulkheadConfigurationProperties

CircuitBreakerConfigurationProperties

RateLimiterProperties

RetryProperties

TimeLimiterProperties

ThreadPoolBulkheadProperties

Annotations

The Micronaut starter provides annotations and AOP Aspects which are auto-configured.
RateLimiter, Retry, CircuitBreaker and Bulkhead annotations support synchronous return types and asynchronous types like CompletableFuture and reactive types like Spring Reactor's Flux and Mono (if you imported appropriate package like resilience4j-reactor).

Bulkhead annotation has a type attribute to define which bulkhead implementation will be used. By default it is semaphore but you can switch to thread pool by setting the type attribute in the annotation:

@Bulkhead(name = BACKEND_A, type = Bulkhead.Type.THREADPOOL)
public CompletableFuture<String> futureFailure() {
    CompletableFuture<String> future = new CompletableFuture<>();
    future.completeExceptionally(new IOException("BAM!"));
    return future;
}

An example of all resilience4j supported spring aspects

@Override
@Bulkhead(name = BACKEND_A, type = Bulkhead.Type.THREADPOOL)
@TimeLimiter(name = BACKEND_A)
@CircuitBreaker(name = BACKEND_A, fallbackMethod = "futureFallback")
public CompletableFuture<String> futureTimeout() {
    Try.run(() -> Thread.sleep(5000));
    return CompletableFuture.completedFuture("Hello World from backend A");
}


 @Executable
 public String fallback() {
    return "Recovered HttpServerErrorException";
 }

@Executable
public CompletableFuture<String> futureFallback() {
    return CompletableFuture.completedFuture("Recovered specific TimeoutException");
}

It's important to remember that a fallback method should be placed in the same class and must be marked with @Executable.

The fallback method has to have the same matching argument signature and the same matching return type.

Aspect order

The Resilience4j Aspects order is following: Retry ( CircuitBreaker ( RateLimiter ( TimeLimiter ( Bulkhead ( Function ) ) ) ) )

There is currently no way to change the order of the executing aspects.

Getting Started


Getting started with resilience4j-micronaut

Suggested Edits are limited on API Reference Pages

You can only suggest edits to Markdown body content, but not to the API spec.