HomeGuidesChangelog
GuidesGitHubLog In

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.