HomeGuidesChangelog
GuidesGitHubLog In
Guides

Getting Started

Getting started with resilience4j-spring-boot2

Setup

Add the Spring Boot 2 Starter of Resilience4j to your compile dependency.

The module expects that org.springframework.boot:spring-boot-starter-actuator and org.springframework.boot:spring-boot-starter-aopare already provided at runtime.

repositories {
    jCenter()
}

dependencies {
  compile "io.github.resilience4j:resilience4j-spring-boot2:${resilience4jVersion}"
  compile('org.springframework.boot:spring-boot-starter-actuator')
  compile('org.springframework.boot:spring-boot-starter-aop')
}

Demo

Setup and usage in Spring Boot 2 is demonstrated into a demo.

Configuration

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

resilience4j.circuitbreaker:
    instances:
        backendA:
            registerHealthIndicator: true
            slidingWindowSize: 100
        backendB:
            registerHealthIndicator: true
            slidingWindowSize: 10
            permittedNumberOfCallsInHalfOpenState: 3
            slidingWindowType: TIME_BASED
            minimumNumberOfCalls: 20
            waitDurationInOpenState: 50s
            failureRateThreshold: 50
            eventConsumerBufferSize: 10
            recordFailurePredicate: io.github.robwin.exception.RecordFailurePredicate
            
resilience4j.retry:
    instances:
        backendA:
            maxRetryAttempts: 3
            waitDuration: 10s
            enableExponentialBackoff: true
            exponentialBackoffMultiplier: 2
            retryExceptions:
                - org.springframework.web.client.HttpServerErrorException
                - java.io.IOException
            ignoreExceptions:
                - io.github.robwin.exception.BusinessException
        backendB:
            maxRetryAttempts: 3
            waitDuration: 10s
            retryExceptions:
                - org.springframework.web.client.HttpServerErrorException
                - java.io.IOException
            ignoreExceptions:
                - io.github.robwin.exception.BusinessException
                
resilience4j.bulkhead:
    instances:
        backendA:
            maxConcurrentCalls: 10
        backendB:
            maxWaitDuration: 10ms
            maxConcurrentCalls: 20
            
resilience4j.thread-pool-bulkhead:
  instances:
    backendC:
      maxThreadPoolSize: 1
      coreThreadPoolSize: 1
      queueCapacity: 1
        
resilience4j.ratelimiter:
    instances:
        backendA:
            limitForPeriod: 10
            limitRefreshPeriod: 1s
            timeoutDuration: 0
            registerHealthIndicator: true
            eventConsumerBufferSize: 100
        backendB:
            limitForPeriod: 6
            limitRefreshPeriod: 500ms
            timeoutDuration: 3s

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

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

Annotations

The Spring Boot2 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 now type attribute the define which bulkhead implementation will be used , by default it is semaphore but if you can switch to thread pool type by setting the type attribute in the annotation :

@Bulkhead(name = BACKEND, type = Bulkhead.Type.THREADPOOL)
 public CompletableFuture<String> doSomethingAsync() throws InterruptedException {
        Thread.sleep(500);
        return CompletableFuture.completedFuture("Test");
    }

An example of all resilience4j supported spring aspects

@CircuitBreaker(name = BACKEND, fallbackMethod = "fallback")
@RateLimiter(name = BACKEND)
@Bulkhead(name = BACKEND)
@Retry(name = BACKEND, fallbackMethod = "fallback")
public Mono<String> method(String param1) {
   return Mono.error(new NumberFormatException());
 }

private Mono<String> fallback(String param1, IllegalArgumentException e) {
  return Mono.just("test");
}

private Mono<String> fallback(String param1, RuntimeException e) {
  return Mono.just("test");
}

It's important to remember that a fallback method should be placed in the same class and must have the same method signature with just ONE extra target exception parameter).

If there are multiple fallbackMethod methods, the method that has the most closest match will be invoked, for example:

If you try to recover from NumberFormatException, the method with signature String fallback(String parameter, IllegalArgumentException exception)} will be invoked.

You can define one global fallback method with an exception parameter only if multiple methods has the same return type and you want to define the same fallback method for them once and for all.

Aspect order

The Resilience4j Aspects order is following:
Retry ( CircuitBreaker ( RateLimiter ( Bulkhead ( Function ) ) ) )
If you need a different order, you can use the functional chaining style instead of the spring annotations style.

Metrics endpoint

CircuitBreaker, Retry, RateLimiter and Bulkhead Metrics are automatically published on the Metrics endpoint. To retrieve the names of the available metrics, make a GET request to /actuator/metrics. Please see Actuator Metrics documentation for more details.

{
    "names": [
        "resilience4j.circuitbreaker.calls",
        "resilience4j.circuitbreaker.buffered.calls",
        "resilience4j.circuitbreaker.state",
        "resilience4j.circuitbreaker.failure.rate"
        ]
}

To retrieve a metric, make a GET request to /actuator/metrics/{metric.name}.
For example: /actuator/metrics/resilience4j.circuitbreaker.calls

{
    "name": "resilience4j.circuitbreaker.calls",
    "measurements": [
        {
            "statistic": "VALUE",
            "value": 3
        }
    ],
    "availableTags": [
        {
            "tag": "kind",
            "values": [
                "not_permitted",
                "successful",
                "failed"
            ]
        },
        {
            "tag": "name",
            "values": [
                "backendB",
                "backendA"
            ]
        }
    ]
}

When you want to publish CircuitBreaker endpoints on the Prometheus endpoint, you have to add the dependency io.micrometer:micrometer-registry-prometheus.

To retrieve metrics, make a GET request to /actuator/prometheus. For more details please see Micrometer Getting Started

Health endpoint

Spring Boot Actuator health information can be used to check the status of your running application. It is often used by monitoring software to alert someone if a production system has serious issues.
By default the CircuitBreaker or RateLimiter health indicators are disabled, but you can enable them via the configuration. Health Indicators are disabled, because the application status is DOWN, when a CircuitBreaker is OPEN. This might not be what you want to achieve.

management.health.circuitbreakers.enabled: true
management.health.ratelimiters.enabled: true

resilience4j.circuitbreaker:
  configs:
    default:
      registerHealthIndicator: true


resilience4j.ratelimiter:
  configs:
    default:
      registerHealthIndicator: true

A closed CircuitBreaker state is mapped to UP, an open state to DOWN and a half-open state to UNKNOWN.

For example:

{
  "status": "UP",
  "details": {
    "circuitBreakers": {
      "status": "UP",
      "details": {
        "backendB": {
          "status": "UP",
          "details": {
            "failureRate": "-1.0%",
            "failureRateThreshold": "50.0%",
            "slowCallRate": "-1.0%",
            "slowCallRateThreshold": "100.0%",
            "bufferedCalls": 0,
            "slowCalls": 0,
            "slowFailedCalls": 0,
            "failedCalls": 0,
            "notPermittedCalls": 0,
            "state": "CLOSED"
          }
        },
        "backendA": {
          "status": "UP",
          "details": {
            "failureRate": "-1.0%",
            "failureRateThreshold": "50.0%",
            "slowCallRate": "-1.0%",
            "slowCallRateThreshold": "100.0%",
            "bufferedCalls": 0,
            "slowCalls": 0,
            "slowFailedCalls": 0,
            "failedCalls": 0,
            "notPermittedCalls": 0,
            "state": "CLOSED"
          }
        }
      }
    }
  }
}

Events endpoint

The emitted CircuitBreaker, Retry, RateLimiter and Bulkhead events are stored in a separate circular event consumer buffers. The size of a event consumer buffer can be configured in the application.yml file (eventConsumerBufferSize).

The endpoint /actuator/circuitbreakers lists the names of all CircuitBreaker instances. The endpoint is also available for Retry, RateLimiter and Bulkhead.
For example:

{
    "circuitBreakers": [
      "backendA",
      "backendB"
    ]
}

The endpoint /management/circuitbreaker-events lists by default the latest 100 emitted events of all CircuitBreaker instances. The endpoint is also available for Retry, RateLimiter and Bulkhead.

{
"circuitBreakerEvents":[
  {
    "circuitBreakerName": "backendA",
    "type": "ERROR",
    "creationTime": "2017-01-10T15:39:17.117+01:00[Europe/Berlin]",
    "errorMessage": "org.springframework.web.client.HttpServerErrorException: 500 This is a remote exception",
    "durationInMs": 0
  },
  {
    "circuitBreakerName": "backendA",
    "type": "SUCCESS",
    "creationTime": "2017-01-10T15:39:20.518+01:00[Europe/Berlin]",
    "durationInMs": 0
  },
  {
    "circuitBreakerName": "backendB",
    "type": "ERROR",
    "creationTime": "2017-01-10T15:41:31.159+01:00[Europe/Berlin]",
    "errorMessage": "org.springframework.web.client.HttpServerErrorException: 500 This is a remote exception",
    "durationInMs": 0
  },
  {
    "circuitBreakerName": "backendB",
    "type": "SUCCESS",
    "creationTime": "2017-01-10T15:41:33.526+01:00[Europe/Berlin]",
    "durationInMs": 0
  }
]
}