| = MicroProfile Fault Tolerance - Retry Policy |
| :index-group: MicroProfile |
| :jbake-type: page |
| :jbake-status: published |
| |
| This is an example of how to use |
| Microprofile @Retry in TomEE. |
| |
| == Retry Feature |
| |
| Microprofile Fault Tolerance has a feature called Retry that can be used |
| to recover an operation from failure, invoking the same operation again |
| until it reaches its stopping criteria. |
| |
| The Retry policy allows to configure : |
| |
| * *maxRetries*: the maximum retries |
| * *delay*: delays between each retry |
| * *delayUnit*: the delay unit |
| * *maxDuration*: maximum duration to perform the retry for. |
| * *durationUnit*: duration unit |
| * *jitter:* the random vary of retry delays |
| * *jitterDelayUnit:* the jitter unit |
| * *retryOn:* specify the failures to retry on |
| * *abortOn:* specify the failures to abort on |
| |
| To use this feature you can annotate a class and/or a method with the |
| @Retry annotation. Check the |
| http://download.eclipse.org/microprofile/microprofile-fault-tolerance-1.1/microprofile-fault-tolerance-spec.html[specification] |
| for more details. |
| |
| == Examples |
| |
| === Run the application |
| |
| [source,java] |
| ---- |
| mvn clean install tomee:run |
| ---- |
| |
| === Example 1 |
| |
| The method statusOfDay will fail three times, each time, throwing a |
| `WeatherGatewayTimeoutException` and as the @Retry annotation is |
| configured to `retryOn` in case of failure, the FailSafe library will |
| take the `maxRetry` value and retry the same operation until it reaches |
| the number maximum of attempts, which is 3 (default value). |
| |
| [source,java] |
| ---- |
| @RequestScoped |
| public class WeatherGateway{ |
| ... |
| @Retry(maxRetry=3, retryOn = WeatherGatewayTimeoutException.class) |
| public String statusOfDay(){ |
| if(counterStatusOfDay.addAndGet(1) <= DEFAULT_MAX_RETRY){ |
| LOGGER.warning(String.format(FORECAST_TIMEOUT_MESSAGE, DEFAULT_MAX_RETRY, counterStatusOfDay.get())); |
| throw new WeatherGatewayTimeoutException(); |
| } |
| return "Today is a sunny day!"; |
| } |
| ... |
| } |
| ---- |
| |
| Day status call |
| |
| [source,java] |
| ---- |
| GET http://localhost:8080/mp-faulttolerance-retry/weather/day/status |
| ---- |
| |
| Server log |
| |
| [source,java] |
| ---- |
| WARNING - Timeout when accessing AccuWeather Forecast Service. Max of Attempts: (3), Attempts: (1) |
| WARNING - Timeout when accessing AccuWeather Forecast Service. Max of Attempts: (3), Attempts: (2) |
| WARNING - Timeout when accessing AccuWeather Forecast Service. Max of Attempts: (3), Attempts: (3) |
| ---- |
| |
| Response |
| |
| [source,java] |
| ---- |
| Today is a sunny day! |
| ---- |
| |
| === Example 2 |
| |
| The method weekStatus will fail two times, each time, throwing a |
| `WeatherGatewayTimeoutException` because `retryOn` is configured and |
| instead of returning a response to the caller, the logic states that at |
| the third attempt, a `WeatherGatewayBusyServiceException` will be |
| thrown. As the `@Retry` annotation is configured to `abortOn` in case of |
| `WeatherGatewayTimeoutException` happens, the remaining attempt won’t be |
| executed and the caller must handle the exception. |
| |
| [source,java] |
| ---- |
| @Retry(maxRetries = 3, retryOn = WeatherGatewayTimeoutException.class, abortOn = WeatherGatewayBusyServiceException.class) |
| public String statusOfWeek(){ |
| if(counterStatusOfWeek.addAndGet(1) <= DEFAULT_MAX_RETRY){ |
| LOGGER.warning(String.format(FORECAST_TIMEOUT_MESSAGE_ATTEMPTS, DEFAULT_MAX_RETRY, counterStatusOfWeek.get())); |
| throw new WeatherGatewayTimeoutException(); |
| } |
| LOGGER.log(Level.SEVERE, String.format(FORECAST_BUSY_MESSAGE, counterStatusOfWeek.get())); |
| throw new WeatherGatewayBusyServiceException(); |
| } |
| ---- |
| |
| Week status call |
| |
| [source,java] |
| ---- |
| GET http://localhost:8080/mp-faulttolerance-retry/weather/week/status |
| ---- |
| |
| Server log |
| |
| [source,java] |
| ---- |
| WARNING - Timeout when accessing AccuWeather Forecast Service. Max of Attempts: (3), Attempts: (1) |
| WARNING - Timeout when accessing AccuWeather Forecast Service. Max of Attempts: (3), Attempts: (2) |
| WARNING - Timeout when accessing AccuWeather Forecast Service. Max of Attempts: (3), Attempts: (3) |
| SEVERE - Error AccuWeather Forecast Service is busy. Number of Attempts: (4) |
| ---- |
| |
| Response |
| |
| [source,java] |
| ---- |
| WeatherGateway Service is Busy. Retry later |
| ---- |
| |
| === Example 3 |
| |
| The `@Retry` annotation allows to configure a delay for each new attempt |
| be executed giving a chance to service requested to recover itself and |
| answerer the request properly. For each new retry follow the delay |
| configure, is needed to set `jitter` to zero (0). Otherwise the delay of |
| each new attempt will be randomized. |
| |
| Analysing the logged messages, is possible to see that all attempts took |
| the pretty much the same time to execute. |
| |
| [source,java] |
| ---- |
| @Retry(retryOn = WeatherGatewayTimeoutException.class, maxRetries = 5, delay = 500, jitter = 0) |
| public String statusOfWeekend() { |
| if (counterStatusOfWeekend.addAndGet(1) <= 5) { |
| logTimeoutMessage(statusOfWeekendInstant); |
| statusOfWeekendInstant = Instant.now(); |
| throw new WeatherGatewayTimeoutException(); |
| } |
| return "The Forecast for the Weekend is Scattered Showers."; |
| } |
| ---- |
| |
| Weekend status call |
| |
| [source,java] |
| ---- |
| GET http://localhost:8080/mp-faulttolerance-retry/weather/weekend/status |
| ---- |
| |
| Server log |
| |
| [source,java] |
| ---- |
| WARNING - Timeout when accessing AccuWeather Forecast Service. |
| WARNING - Timeout when accessing AccuWeather Forecast Service. Delay before this attempt: (501) millis |
| WARNING - Timeout when accessing AccuWeather Forecast Service. Delay before this attempt: (501) millis |
| WARNING - Timeout when accessing AccuWeather Forecast Service. Delay before this attempt: (501) millis |
| WARNING - Timeout when accessing AccuWeather Forecast Service. Delay before this attempt: (500) millis |
| ---- |
| |
| === Example 4 |
| |
| Basically with the same behaviour of the `Example 3`, this example sets |
| the `delay` and `jitter` with 500 millis to randomly create a new delay |
| for each new attempt after the first failure. |
| https://github.com/jhalterman/failsafe/blob/master/src/main/java/net/jodah/failsafe/AbstractExecution.java[AbstractExecution#randomDelay(delay,jitter,random)] |
| can give a hit of how the new delay is calculated. |
| |
| Analysing the logged messages, is possible to see how long each attempt |
| had to wait until its execution. |
| |
| [source,java] |
| ---- |
| @Retry(retryOn = WeatherGatewayTimeoutException.class, delay = 500, jitter = 500) |
| public String statusOfMonth() { |
| if (counterStatusOfWeekend.addAndGet(1) <= DEFAULT_MAX_RETRY) { |
| logTimeoutMessage(statusOfMonthInstant); |
| statusOfMonthInstant = Instant.now(); |
| throw new WeatherGatewayTimeoutException(); |
| } |
| return "The Forecast for the Weekend is Scattered Showers."; |
| } |
| ---- |
| |
| Month status call |
| |
| [source,java] |
| ---- |
| GET http://localhost:8080/mp-faulttolerance-retry/weather/month/status |
| ---- |
| |
| Server log |
| |
| [source,java] |
| ---- |
| WARNING - Timeout when accessing AccuWeather Forecast Service. |
| WARNING - Timeout when accessing AccuWeather Forecast Service. Delay before this attempt: (417) millis |
| WARNING - Timeout when accessing AccuWeather Forecast Service. Delay before this attempt: (90) millis |
| ---- |
| |
| === Example 5 |
| |
| If a condition for an operation be re-executed is not set as in the |
| previous examples using the parameter `retryOn`, the operation is |
| executed again for _any_ exception that is thrown. |
| |
| [source,java] |
| ---- |
| @Retry(maxDuration = 1000) |
| public String statusOfYear(){ |
| if (counterStatusOfWeekend.addAndGet(1) <= 5) { |
| logTimeoutMessage(statusOfYearInstant); |
| statusOfYearInstant = Instant.now(); |
| throw new RuntimeException(); |
| } |
| return "WeatherGateway Service Error"; |
| } |
| ---- |
| |
| Year status call |
| |
| [source,java] |
| ---- |
| GET http://localhost:8080/mp-faulttolerance-retry/weather/year/statusk |
| ---- |
| |
| Server log |
| |
| [source,java] |
| ---- |
| WARNING - Timeout when accessing AccuWeather Forecast Service. |
| WARNING - Timeout when accessing AccuWeather Forecast Service. Delay before this attempt: (666) millis |
| WARNING - Timeout when accessing AccuWeather Forecast Service. Delay before this attempt: (266) millis |
| WARNING - Timeout when accessing AccuWeather Forecast Service. Delay before this attempt: (66) millis |
| ---- |
| |
| === Run the tests |
| |
| You can also try it out using the |
| link:src/test/java/org/superbiz/rest/WeatherServiceTest.java[WeatherServiceTest.java] |
| available in the project. |
| |
| [source,java] |
| ---- |
| mvn clean test |
| ---- |
| |
| [source,java] |
| ---- |
| [INFO] Results: |
| [INFO] |
| [INFO] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0 |
| ---- |