| = MicroProfile Fault Tolerance - Retry Policy |
| :index-group: MicroProfile |
| :jbake-type: page |
| :jbake-status: published |
| |
| Este é um exemplo de como usar |
| Microprofile @Retry em TomEE. |
| |
| == Retry Feature |
| |
| Microprofile Fault Tolerance tem um recurso chamado Retry que pode ser usado |
| para recuperar uma operação de falha, chamando a mesma operação novamente |
| até atingir seus critérios de parada. |
| |
| A Retry policy permite configurar : |
| |
| * *maxRetries*: o máximo de tentativas |
| * *delay*: atrasos entre cada tentativa |
| * *delayUnit*: a unidade de atraso |
| * *maxDuration*: duração máxima para executar a nova tentativa |
| * *durationUnit*: unidade de duração |
| * *jitter:* a variação aleatória dos atrasos de uma nova tentativa |
| * *jitterDelayUnit:* a unidade de instabilidade |
| * *retryOn:* especifique as falhas para tentar novamente |
| * *abortOn:* especifique as falhas para abortar |
| |
| Para usar esse recurso, você pode anotar uma classe e/ou um método com a anotação @Retry. Verifique em |
| http://download.eclipse.org/microprofile/microprofile-fault-tolerance-1.1/microprofile-fault-tolerance-spec.html[specification] |
| para mais detalhes. |
| |
| == Exemplos |
| |
| === Execute o aplicativo |
| |
| [source,java] |
| ---- |
| mvn clean install tomee:run |
| ---- |
| |
| === Exemplo 1 |
| |
| O método statusOfDay falhará 3 (três) vezes, a cada vez, lançando uma |
| `WeatherGatewayTimeoutException` e como a anotação @Retry está |
| configurada para `retryOn` em caso de falha, a biblioteca FailSafe pegará |
| o valor `maxRetry` e tentará a mesma operação até atingir o número máximo de tentativas, que é 3 (valor padrão). |
| |
| [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!"; |
| } |
| ... |
| } |
| ---- |
| |
| Chamada de status do dia |
| |
| [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! |
| ---- |
| |
| === Exemplo 2 |
| |
| O método weekStatus falhará duas vezes, a cada vez, lançando uma |
| `WeatherGatewayTimeoutException` porque `retryOn` está configurado e |
| em vez de retornar uma resposta ao chamador, a lógica afirma que na terceira tentativa uma |
| `WeatherGatewayBusyServiceException` será lançada. Como a anotação `@Retry` está configurada para |
| `abortOn` no caso de `WeatherGatewayTimeoutException` ocorrer, a tentativa restante não será |
| executada e o chamador deve lidar com a exceção. |
| |
| [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(); |
| } |
| ---- |
| |
| Chamada de status da semana |
| |
| [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 |
| ---- |
| |
| === Exemplo 3 |
| |
| A anotação `@Retry` permite configurar um atraso para cada nova tentativa |
| ser executada, dando a chance ao serviço solicitado para se recuperar e |
| responder a solicitação corretamente. Para cada nova tentativa, seguindo o atraso |
| configurado, é necessário definir `jitter` como zero (0). Caso contrário, o atraso de |
| cada nova tentativa será aleatório. |
| |
| Analisando o log de mensagens, é possível ver que todas as tentativas levaram |
| praticamente o mesmo tempo para executar. |
| |
| [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."; |
| } |
| ---- |
| |
| Chamada de status de fim de semana |
| |
| [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 |
| ---- |
| |
| === Exemplo 4 |
| |
| Basicamente com o mesmo comportamento do `Exemplo 3`, este exemplo defini |
| o `delay` e o `jitter` com 500 milissegundos para aleatoriamente um novo atraso a cada nova tentativa |
| após a primeira falha. |
| https://github.com/jhalterman/failsafe/blob/master/src/main/java/net/jodah/failsafe/AbstractExecution.java[AbstractExecution#randomDelay(delay,jitter,random)] |
| pode dar uma ideia de como o novo atraso é calculado. |
| |
| Analisando o log de mensagens, é possível ver quanto tempo cada tentativa teve |
| que esperar até a sua execução. |
| |
| [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."; |
| } |
| ---- |
| |
| Chamada de status do mês |
| |
| [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 |
| |
| Se uma condição para uma operação re-executada não estiver definida como nos |
| exemplos anteriores usando o parâmetro `retryOn`, a operação é |
| executada novamente para qualquer exceção lançada. |
| |
| [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"; |
| } |
| ---- |
| |
| Chamada de status do ano |
| |
| [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 |
| ---- |
| |
| === Execute os testes |
| |
| Você também pode experimentar usando |
| link:src/test/java/org/superbiz/rest/WeatherServiceTest.java[WeatherServiceTest.java] |
| disponível no projeto. |
| |
| [source,java] |
| ---- |
| mvn clean test |
| ---- |
| |
| [source,java] |
| ---- |
| [INFO] Results: |
| [INFO] |
| [INFO] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0 |
| ---- |