mvn clean install tomee:run
MicroProfile Fault Tolerance - Retry Policy
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
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