Preloader image

Este es un ejemplo de cómo usar Microprofile @Retry en TomEE.

Retry Feature

Microprofile Fault Tolerance tiene una función llamada Reintentar que se puede utilizar para recuperase de una operación del error, invocando la misma operación de nuevo hasta que se alcancen los criterios de detención.

La directiva de reintento permite configurar:

  • maxRetries: los reintentos máximos

  • delay: retrasos entre cada reintento

  • delayUnit: la unidad de retardo

  • maxDuration: duración máxima para realizar el reintento.

  • durationUnit: unidad de duración

  • jitter: la variación aleatoria de retrasos de reintento

  • jitterDelayUnit: la unidad de fluctuación

  • retryOn: especifica reintentar en caso de errores

  • abortOn: especifica anular en caso de errores

Para utilizar esta función puede anotar una clase y/o método con la anotación @Retry. Compruebe la especificación para más detalles.

Ejemplos

Ejecutar la aplicación

mvn clean install tomee:run

Ejemplo 1

El método statusOfDay producirá un error tres veces, cada vez, lanzando una 'WeatherGatewayTimeoutException' y como la anotación @Retry está configurada para retryOn en caso de error, la libreria FailSafe tomará el valor maxRetry y volverá a intentar la misma operación hasta que alcance el número máximo de intentos, que es 3 (valor predeterminado).

@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!";
   }
   ...
 }

Llamada de estado del día

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

Registro del servidor

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)

Respuesta

Today is a sunny day! (¡Hoy es un día soleado!)

Ejemplo 2

El método weekStatus fallará dos veces, cada vez, lanzando una excepción WeatherGatewayTimeoutException porque retryOn está configurado y en lugar de devolver una respuesta al autor de la llamada, la lógica indica que en el tercer intento, una excepción WeatherGatewayBusyServiceException será lanzada. Como la anotación @Retry está configurada para abortOn en caso que WeatherGatewayTimeoutException ocurra, el intento restante no será ejecutado y el autor de la llamada tendra que controlar la excepción.

@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();
}

Llamada de estado de la semana

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

Registro del servidor

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)

Respuesta

WeatherGateway Service is Busy. Retry later

Ejemplo 3

La anotación @Retry permite configurar un retraso para ejecutar cada nuevo intento dando la oportunidad al servicio solicitado para recuperarse y contestador de la solicitud correctamente. Para cada nuevo reintento siga el retraso configurar, es necesario establecer jitter a cero (0). De lo contrario, el retraso de cada nuevo intento será aleatorio.

Analizando los mensajes registrados, es posible ver que todos los intentos toman más o menos el mismo tiempo para ejecutar.

@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.";
}

Llamada de estado de la semana

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

Registro del servidor

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

Ejemplo 4

Básicamente con el mismo comportamiento del Ejemplo 3, este ejemplo establece el delay y el jitter con 500 milisegundos para crear aleatoriamente un nuevo retardo para cada nuevo intento después del primer error. AbstractExecution-randomDelay(delay,jitter,random) puede dar una idea de cómo se calcula el nuevo retraso.

Mediante el análisis de los mensajes registrados, es posible ver cuanto cada intento tiene que esperar antes de ejecutarse.

@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.";
}

Llamada de estado del mes

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

Registro del servidor

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

Ejemplo 5

Si no se establece una condición para que una operación que se vuelva a ejecutar como en los ejemplos anteriores mediante el parámetro retryOn, la operación se ejecuta de nuevo para cualquier excepción que se produce.

@Retry(maxDuration = 1000)
public String statusOfYear(){
    if (counterStatusOfWeekend.addAndGet(1) <= 5) {
        logTimeoutMessage(statusOfYearInstant);
        statusOfYearInstant = Instant.now();
        throw new RuntimeException();
    }
    return "WeatherGateway Service Error";
}

Llamada de estado del año

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

Registro del servidor

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

Ejecutar las pruebas

También puede probarlo utilizando el enlace:src/test/java/org/superbiz/rest/WeatherServiceTest.java[WeatherServiceTest.java] disponible en el proyecto.

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