Resilience4j is a lightweight fault tolerance library inspired by Netflix Hystrix, but designed for Java 8 and functional programming. Lightweight, because the library only uses Vavr, which does not have any other external library dependencies. Netflix Hystrix, in contrast, has a compile dependency to Archaius which has many more external library dependencies such as Guava and Apache Commons Configuration.
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 = () -> backendService.doSomething(param1, param2);
Supplier<String> decoratedSupplier = Decorators.ofSupplier(supplier)
.withRetry(Retry.ofDefaults("name"))
.withCircuitBreaker(CircuitBreaker.ofDefaults("name"))
.withBulkhead(Bulkhead.ofDefaults("name"));
String result = Try.ofSupplier(decoratedSupplier)
.recover(throwable -> "Hello from Recovery").get();
// 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(supplier);
With Resilience4j you don’t have to go all-in, you can pick what you need.
Setup and usage is described in our User Guide.
Resilience4j provides several core modules and add-on modules:
-
resilience4j-circuitbreaker: Circuit breaking
-
resilience4j-ratelimiter: Rate limiting
-
resilience4j-bulkhead: Bulkheading
-
resilience4j-retry: Automatic retrying (sync and async)
-
resilience4j-timelimiter: Timeout handling
-
resilience4j-cache: Result caching
-
resilience4j-retrofit: Retrofit adapter
-
resilience4j-feign: Feign adapter
-
resilience4j-consumer: Circular Buffer Event consumer
-
resilience4j-kotlin: Kotlin coroutines support
-
resilience4j-spring-boot: Spring Boot Starter
-
resilience4j-spring-boot2: Spring Boot 2 Starter
-
resilience4j-spring-boot-cloud: Spring Cloud Config Server
-
resilience4j-spring-boot-cloud2: Spring Cloud 2 Config Server
-
resilience4j-ratpack: Ratpack Starter
-
resilience4j-vertx: Vertx Future decorator
-
resilience4j-rxjava2: Custom RxJava2 operators
-
resilience4j-reactor: Custom Spring Reactor operators
name | how does it work? | description | slogans | links |
---|---|---|---|---|
Retry |
repeats failed executions |
Many faults are transient and may self-correct after a short delay. |
"Insert coin to try again", |
|
Circuit Breaker |
temporary blocks possible failures |
When a system is seriously struggling, failing fast is better than making clients wait. |
"Stop doing it if it hurts", |
|
Rate Limiter |
limits executions/period |
Prepare for a scale and establish reliability and HA of your service. |
"That’s enough for this minute!", |
|
Time Limiter |
limits duration of execution |
Beyond a certain wait, a success result is unlikely. |
"Don’t wait forever" |
|
Bulkhead |
limits concurrent executions |
Resources are isolated into pools so that if one fails, the others will continue. |
"One fault shouldn’t sink the whole ship", |
|
Cache |
memorizes a successful result |
Some proportion of requests may be similar. |
"You’ve asked that one before" |
|
Fallback |
provides an alternative result for failures |
Things will still fail - plan what you will do when that happens. |
"Degrade gracefully", |
Above table is based on Polly: resilience policies.
Setup and usage in Spring Boot 2 is demonstrated here.
The following example shows how to decorate a lambda expression (Supplier) with a CircuitBreaker and how to retry the call at most 3 times when an exception occurs.
You can configure the wait interval between retries and also configure a custom backoff algorithm.
The example uses Vavr’s Try Monad to recover from an exception and invoke another lambda expression as a fallback, when even all retries have failed.
// Simulates a Backend Service
public interface BackendService {
String doSomething();
}
// Create a CircuitBreaker (use default configuration)
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("backendName");
// Create a Retry with at most 3 retries and a fixed time interval between retries of 500ms
Retry retry = Retry.ofDefaults("backendName");
// Decorate your call to BackendService.doSomething() with a CircuitBreaker
Supplier<String> decoratedSupplier = CircuitBreaker
.decorateSupplier(circuitBreaker, backendService::doSomething);
// Decorate your call with automatic retry
decoratedSupplier = Retry
.decorateSupplier(retry, decoratedSupplier);
// Execute the decorated supplier and recover from any exception
String result = Try.ofSupplier(decoratedSupplier)
.recover(throwable -> "Hello from Recovery").get();
// 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);
The following example shows how to decorate an Observable by using the custom RxJava operator.
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("testName");
Observable.fromCallable(backendService::doSomething)
.compose(CircuitBreakerOperator.of(circuitBreaker))
Note
|
Resilience4j also provides RxJava operators for RateLimiter , Bulkhead , TimeLimiter and Retry . Find out more in our User Guide
|
The following example shows how to decorate a Mono by using the custom Reactor operator.
CircuitBreaker circuitBreaker = CircuitBreaker.ofDefaults("testName");
Mono.fromCallable(backendService::doSomething)
.compose(CircuitBreakerOperator.of(circuitBreaker))
Note
|
Resilience4j also provides Reactor operators for RateLimiter , Bulkhead , TimeLimiter and Retry . Find out more in our User Guide
|
The following example shows how to restrict the calling rate of some method to be not higher than 1 req/sec.
// Create a custom RateLimiter configuration
RateLimiterConfig config = RateLimiterConfig.custom()
.timeoutDuration(Duration.ofMillis(100))
.limitRefreshPeriod(Duration.ofSeconds(1))
.limitForPeriod(1)
.build();
// Create a RateLimiter
RateLimiter rateLimiter = RateLimiter.of("backendName", config);
// Decorate your call to BackendService.doSomething()
Supplier<String> restrictedSupplier = RateLimiter
.decorateSupplier(rateLimiter, backendService::doSomething);
// First call is successful
Try<String> firstTry = Try.ofSupplier(restrictedSupplier);
assertThat(firstTry.isSuccess()).isTrue();
// Second call fails, because the call was not permitted
Try<String> secondTry = Try.of(restrictedSupplier);
assertThat(secondTry.isFailure()).isTrue();
assertThat(secondTry.getCause()).isInstanceOf(RequestNotPermitted.class);
There are two isolation strategies and bulkhead implementations.
The following example shows how to decorate a lambda expression with a Bulkhead. A Bulkhead can be used to limit the amount of parallel executions. This bulkhead abstraction should work well across a variety of threading and io models. It is based on a semaphore, and unlike Hystrix, does not provide "shadow" thread pool option.
// Create a custom Bulkhead configuration
BulkheadConfig config = BulkheadConfig.custom()
.maxConcurrentCalls(150)
.maxWaitTime(100)
.build();
Bulkhead bulkhead = Bulkhead.of("backendName", config);
Supplier<String> supplier = Bulkhead
.decorateSupplier(bulkhead, backendService::doSomething);
The following example shows how to use a lambda expression with a ThreadPoolBulkhead which uses a bounded queue and a fixed thread pool.
// Create a custom ThreadPoolBulkhead configuration
ThreadPoolBulkheadConfig config = ThreadPoolBulkheadConfig.custom()
.maxThreadPoolSize(10)
.coreThreadPoolSize(2)
.queueCapacity(20)
.build();
ThreadPoolBulkhead bulkhead = ThreadPoolBulkhead.of("backendName", config);
// Decorate or execute immediately a lambda expression with a ThreadPoolBulkhead.
Supplier<CompletionStage<String>> supplier = ThreadPoolBulkhead
.decorateSupplier(bulkhead, backendService::doSomething);
CompletionStage<String> execution = bulkhead
.executeSupplier(backendService::doSomething);
CircuitBreaker
, RateLimiter
, Cache
, Bulkhead
, TimeLimiter
and Retry
components emit a stream of events. It can be consumed for logging, assertions and any other purpose.
A CircuitBreakerEvent
can be a state transition, a circuit breaker reset, a successful call, a recorded error or an ignored error. All events contains additional information like event creation time and processing duration of the call. If you want to consume events, you have to register an event consumer.
circuitBreaker.getEventPublisher()
.onSuccess(event -> logger.info(...))
.onError(event -> logger.info(...))
.onIgnoredError(event -> logger.info(...))
.onReset(event -> logger.info(...))
.onStateTransition(event -> logger.info(...));
// Or if you want to register a consumer listening to all events, you can do:
circuitBreaker.getEventPublisher()
.onEvent(event -> logger.info(...));
You can use RxJava or Spring Reactor Adapters to convert the EventPublisher
into a Reactive Stream. The advantage of a Reactive Stream is that you can use RxJava’s observeOn
operator to specify a different Scheduler that the CircuitBreaker will use to send notifications to its observers/consumers.
RxJava2Adapter.toFlowable(circuitBreaker.getEventPublisher())
.filter(event -> event.getEventType() == Type.ERROR)
.cast(CircuitBreakerOnErrorEvent.class)
.subscribe(event -> logger.info(...))
Note
|
You can also consume events from other components. Find out more in our User Guide |
-
Deutsche Telekom (In an application with over 400 million request per day)
-
AOL (In an application with low latency requirements)
-
Netpulse (In system with 40+ integrations)
-
wescale.de (In a B2B integration platform)
-
Topia (In an HR application built with microservices architecture)
-
Auto Trader Group plc (UK’s largest digital automotive marketplace)
-
PlayStation Network (Platform backend)
Copyright 2019 Robert Winkler, Bohdan Storozhuk, Mahmoud Romeh and Dan Maas
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.