We will go through specific module(Resilience4j-Retry) from Resilience4j which is a fault tolerance library designed for Java8 and functional programming and it is lightweight library with minimal dependencies (mainly vavr)
When you retry , there are many cases for example :
- cross micro services communication for remote systems calls which most likely will need circuit breaker logic added as well
- if you have business or functional logic that need to get a consistent end state and most likely it is asynchronous Flow
Ok , what you need to do to start using resileience4j retry :
if you are using maven:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<dependency> | |
<groupId>io.github.resilience4j</groupId> | |
<artifactId>resilience4j-retry</artifactId> | |
<version>0.13.2</version> | |
</dependency> | |
<dependency> | |
<groupId>io.github.resilience4j</groupId> | |
<artifactId>resilience4j-core</artifactId> | |
<version>0.13.2</version> | |
</dependency> |
if you are using gradle :
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
compile "io.github.resilience4j:resilience4j-core:0.13.2" | |
compile "io.github.resilience4j:resilience4j-retry:0.13.2" |
What can be covered by the retry module of resilience4j :
- Synchronous retry and Asynchronous retry
- Rety on exceptions or response predicate which can be useful if you want to retry on specific response value not just thrown exceptions
- Back-off strategy for the retry configuration plus max retry attempts
- Ignoring set of exceptions to not retry on
- It has support for checked(exception handling added) and unchecked functions executions (ex Function , Supplier , Callable , Runnable..)
- it could be integrated with spring if needed.
Now showing examples of the mentioned features up:
How to configure the Asynchronous retry , full code for testing resilience4j retry on Github
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Given the HelloWorldService returns Hello world | |
given(helloWorldService.returnHelloWorld()) | |
.willReturn(completedFuture("Hello world")); | |
final AsyncRetry retryContext = AsyncRetry.of("retryConfig", | |
// we set the response type to String | |
RetryConfig.<String>custom() | |
// max retry attempts | |
.maxAttempts(3) | |
// what are the ignore exception to no retry on | |
.ignoreExceptions(IllegalStateException.class) | |
// what are the exceptions to try on | |
.retryExceptions(TimeoutException.class) | |
// retry if the response contains world | |
.retryOnResult(s -> s.contains("world")) | |
// retry backoff strategy, IntervalFunction has many built in interface functions you can check it out | |
.intervalFunction(IntervalFunction.ofExponentialBackoff()) | |
.build()); | |
// Decorate the invocation of the HelloWorldService | |
Supplier<CompletionStage<String>> supplier = AsyncRetry.decorateCompletionStage( | |
retryContext, | |
scheduler, | |
() -> helloWorldService.returnHelloWorld()); | |
// When | |
String result = awaitResult(supplier); | |
// Then the helloWorldService should be invoked 1 time | |
BDDMockito.then(helloWorldService).should(Mockito.times(3)).returnHelloWorld(); | |
Assertions.assertEquals(result, "Hello world"); |
For Synchronous calls , you have many options (Supplier , Callable , Function , plus Checked version of them, please check resilience4j retry APIs for more information) :
Example :
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Given the HelloWorldService returns Hello world | |
BDDMockito.given(helloWorldService.returnHelloWorld()).willReturn("Hello world"); | |
// Create a Retry with default configuration | |
final RetryConfig tryAgain = RetryConfig.<String>custom().retryOnResult(s -> s.contains("Hello world")) | |
.maxAttempts(2).build(); | |
Retry retry = Retry.of("id", tryAgain); | |
// Decorate the invocation of the HelloWorldService | |
Supplier<String> supplier = Retry.decorateSupplier(retry, helloWorldService::returnHelloWorld); | |
// When | |
String result = supplier.get(); | |
// Then the helloWorldService should be invoked 1 time | |
BDDMockito.then(helloWorldService).should(Mockito.times(2)).returnHelloWorld(); | |
assertThat(result).isEqualTo("Hello world"); |
For more information , please check resilience4j Github and the code samples on GitHub as well .
In the next blogs I will cover spring and spring boot integration .
References :