Preloader image

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 specification para mais detalhes.

Exemplos

Execute o aplicativo

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).

@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

GET http://localhost:8080/mp-faulttolerance-retry/weather/day/status

Server log

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

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.

@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

GET http://localhost:8080/mp-faulttolerance-retry/weather/week/status

Server log

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

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.

@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

GET http://localhost:8080/mp-faulttolerance-retry/weather/weekend/status

Server log

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. 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.

@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

GET http://localhost:8080/mp-faulttolerance-retry/weather/month/status

Server log

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.

@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

GET http://localhost:8080/mp-faulttolerance-retry/weather/year/statusk

Server log

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 WeatherServiceTest.java disponível no projeto.

mvn clean test
[INFO] Results:
[INFO]
[INFO] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0