In this article we will look at very simple basic example of Resilience4j retry feature & look at runtime behavior of retry. Here is the maven dependency for resilience4j-retry required for this example.
Retry Concept
Sometimes there might be intermittent failures which are might not be long running issue but might be a glitch or temporary problems. In such cases, call can just be made again & mostly it will end up in success.
Example in this article
Retry Configurations: Retry configuration which will try service call 2 times in total. Retry will be done if result is “FAILURE” or if BadProcessingException is thrown.
- Create mock external service which intermittently fails or throws exception (randomly for test purpose)
- Make 20 calls to the service so that few might end up in failure or exceptions.
- We will observe how retry behaves for all threads.
Mock Service
Here is a mock service that will randomly return SUCCESS or FAILURE or throw BadProcessingException.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
/* * This service will randomly return SUCCESS or FAILURE or throw * BadProcessingException. */ class ExternalIntermittentFailingService { public String callService() { double random = Math.random(); if (random < 0.6) { System.out.println("\tProcessing finished. Status = SUCCESS"); return "SUCCESS"; } else if (random < 0.8) { System.out.println("\tProcessing finished. Status = FAILURE"); return "FAILURE"; } else { System.out.println("\tProcessing finished. Status = BadProcessingException"); throw new BadProcessingException("Bad processing"); } } } |
Retry in action
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
public class RetryBasics { public static void main(String[] args) throws InterruptedException { /* * Retry configuration which will try service call 2 times in total. Retry will * be done if result is "FAILURE" or if BadProcessingException is thrown. */ RetryConfig config = RetryConfig.custom().maxAttempts(2).waitDuration(Duration.ofMillis(1000)) .retryOnResult(response -> response.equals("FAILURE")) .retryOnException(e -> e instanceof BadProcessingException).build(); // Create a RetryRegistry with a custom global configuration RetryRegistry registry = RetryRegistry.of(config); // Get or create a Retry from the registry - Retry retry = registry.retry("externalIntermittentFailingService"); ExternalIntermittentFailingService service = new ExternalIntermittentFailingService(); // Make 20 calls for (int i = 0; i < 20; i++) { System.out.println(">> Call count = " + (i + 1)); try { String finalResult = retry.executeSupplier(() -> service.callService()); System.out.println("\tFinal Result = " + finalResult); } catch (Exception e) { e.printStackTrace(); } } } } |
Output
Key behavior to look for in output
- Success in first attempt (Call count = 2):
- Call succeeds in first attempt, then simply result will be returned..
- Exception first & retry gets success (Call count = 1):
- Call ends up with BadProcessingException. Retry attempt is made & result is success.
- FAILURE first & retry gets success (Call count = 9):
- Call returns FAILURE status. Retry attempt is made & result is success.
- FAILURE first & retry also gets FAILURE (Call count = 10):
- Call returns FAILURE status. Retry attempt is made & result is again FAILURE, so this is considered as final status.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
>> Call count = 1 Processing finished. Status = BadProcessingException Processing finished. Status = SUCCESS Final Result = SUCCESS >> Call count = 2 Processing finished. Status = SUCCESS Final Result = SUCCESS >> Call count = 3 Processing finished. Status = SUCCESS Final Result = SUCCESS >> Call count = 4 Processing finished. Status = SUCCESS Final Result = SUCCESS >> Call count = 5 Processing finished. Status = SUCCESS Final Result = SUCCESS >> Call count = 6 Processing finished. Status = SUCCESS Final Result = SUCCESS >> Call count = 7 Processing finished. Status = SUCCESS Final Result = SUCCESS >> Call count = 8 Processing finished. Status = SUCCESS Final Result = SUCCESS >> Call count = 9 Processing finished. Status = FAILURE Processing finished. Status = SUCCESS Final Result = SUCCESS >> Call count = 10 Processing finished. Status = FAILURE Processing finished. Status = FAILURE Final Result = FAILURE >> Call count = 11 Processing finished. Status = SUCCESS Final Result = SUCCESS >> Call count = 12 Processing finished. Status = BadProcessingException Processing finished. Status = SUCCESS Final Result = SUCCESS >> Call count = 13 Processing finished. Status = BadProcessingException Processing finished. Status = SUCCESS Final Result = SUCCESS >> Call count = 14 Processing finished. Status = SUCCESS Final Result = SUCCESS >> Call count = 15 Processing finished. Status = BadProcessingException Processing finished. Status = SUCCESS Final Result = SUCCESS >> Call count = 16 Processing finished. Status = BadProcessingException Processing finished. Status = SUCCESS Final Result = SUCCESS >> Call count = 17 Processing finished. Status = BadProcessingException Processing finished. Status = SUCCESS Final Result = SUCCESS >> Call count = 18 Processing finished. Status = SUCCESS Final Result = SUCCESS >> Call count = 19 Processing finished. Status = SUCCESS Final Result = SUCCESS >> Call count = 20 Processing finished. Status = FAILURE Processing finished. Status = FAILURE Final Result = FAILURE |
Further reading
Resilience4j Complete Tutorial | Basics with runtime behavior | Simple examples for beginners