Política de reintentos con Spring Retry

0
6550

Política de reintentos con Spring-retry

Índice de contenidos

1. Introducción
2. Entorno
3. Solución Retry template
4. Solución AOP
5. Conclusiones
6. Referencias

1. Introducción

Definir una política de reintentos es una necesidad bastante frecuente en aplicaciones empresariales. La integración con un sistema externo, atacar a servicios web, el envío a colas JMS u operaciones con base de datos
son algunos ejemplos de causas que pueden necesitar la implementación de una política de reintentos.

Implementar una política de reintentos personalizada es la solución más solicitada en la mayoría de aplicaciones. Un ejemplo de ello lo podemos encontrar aquí.
Pero al final estas soluciones no dejan de ser implementaciones específicas de las aplicaciones, y no suponen una solución estandarizada a los reintentos de operaciones en aplicaciones.

Spring-retry es un módulo de Spring que hasta la versión 2.2.0 estaba integrado dentro de spring-batch, y que ahora se define como componente independiente dentro de la jerarquía de Spring. Dicho módulo nos ofrece una solución bastante sencilla de implementar e integrar políticas de reintentos en nuestras aplicaciones JEE. Ofrece una gran versatilidad de configuraciones, pudiendo decidir el número de reintentos por operación, timeouts, estados entre reintentos, etc.
Además, como veremos más adelante, se integra de forma muy sencilla con nuestras aplicaciones Spring, y se puede externalizar su configuración si usamos la solución con AOP, al estar integrada en los propios archivos de configuración de contexto de Spring.

Elementos esenciales en Spring-retry

Cuando necesitamos realizar una operación que exija definir una política de reintentos cuando la operación falle, como puede ser por ejemplo reintentar la consulta a un servicio REST, o reintentar el envío de un correo electrónico,
tendremos que automatizarla con la gestión de una serie de elementos básicos de Spring-retry. Dichos elementos son:

  • Interfaz RetryOperations. Tiene la siguiente estructura:


    Como podemos observar, se definen cuatro métodos execute, que se ejecutarán cuando el método al que ‘escuchan’ lanza una excepción. Los métodos de RetryOperations reciben los siguientes parámetros:

  • Callback: Ejecución de reintento. Puede ser de dos tipos:
    • RetryCallBack: Callback de reintento (cuando se ha ejecutado un reintento). Se define para la ejecución de un post procesamiento cuando se realiza un reintento (después de que el método observado lance un java.lang.Exception)
    • RecoveryCallBack: Callback de recuperación (cuando se han terminado los reintentos). Se define para la ejecución de un post procesamiento cuando se agotan los intentos (dependiendo de la política definida).
  • RetryState: Algunas políticas de reintentos requieren que se guarde un estado entre intentos (por ejemplo, cuando tenemos variables que cambian entre la ejecución del método, y necesitamos almacenarlas por cualquier motivo).
    Spring-batch nos ofrece una clase RetryState para poder almacenar dicha información de estado entre ejecuciones. Para más información sobre cómo se almacena dicha información, ver el apartado de referencias

2. Entorno

  • Macbook pro core i7 con 16gb RAM
  • SO: Yosemite
  • IDE: Spring Tool Suite 3.4.0 RELEASE
  • Apache Maven 3.1.1

3. Solución Retry template

En este apartado vamos a dar una solución al siguiente problema:

– Queremos enviar un correo electrónico a un destinatario cualquiera, pero necesitamos que si el envío falla (por cualquier razón: El servidor de correo está caído, se produce un timeout en el intento de envío, etc.) se reintente un máximo de 5 veces.
Para no sobresaturar al servidor de correo, necesitamos un período de 5 segundos entre reintentos de envío.

Vamos a dar una solución con spring-retry basado en RetryTemplate. Lo primero es añadir las siguientes dependencias a nuestro proyecto Maven:

A continuación, vamos a crear nuestro servicio de Spring que va a tener un método sendMail. Dicho método lanzará una excepción las 4 primeras veces que se ejecute, y a la quinta simulará el envío de un correo electrónico:

En el test de este servicio, vamos a definir un nuevo RetryTemplate con las siguientes características:

  • Tendrá una política de reintentos (RetryPolicy), que recibe:
    • Un entero indicando el número de reintentos, en nuestro caso 5
    • Un mapa donde se registrarán aquellas excepciones que soporta la política, es decir, comprobará la excepción que ha lanzado el método sobre el que realiza los intentos, y la comparará con las del mapa. Si coincide alguna, tratará el reintento
  • Tendrá una política de BackOff. En Spring-retry tenemos algunas políticas de BackOff, según las necesidades de negocio. Entre ellas, cabe destacar:
    • ExponentialBackOffPolicy: Define una política de reintentos exponencial (por ejemplo, intervalos de 2 segundos, con un multiplicador de 1.5 hasta alcanzar 20 segundos)
    • FixedBackOffPolicy: Política de reintentos clásica con timeout

    Se usará para este ejemplo una política FixedBackOffPolicy, que establecerá un timeout entre reintentos de 5 segundos.

  • Definirá un método RetryCallBack que se ejecutará cuando se lance una excepción en el método sobre el que realiza los intentos

El test por tanto es el siguiente:

En el test verificamos que el método sendMail ha sido invocado 5 veces. La ejecución del mismo provoca la siguiente salida en el LOG:

4. Solución AOP

La solución que se propone en este apartado es una variante de la anterior, teniendo en cuenta que, en lugar de usar RetryTemplate, se da una solución basada en AOP.

Para ello, definimos el siguiente archivo de configuración de spring:

Como podemos observar, estamos definiendo tanto las políticas como el template con clases de soporte de Spring-retry. Además, definimos un Advice, que enlazará con la política de reintentos cuando se ejecute el pointcut definido

Definimos el test como sigue:

El test anterior levanta el contexto de Spring, realiza una llamada a sendMail y comprueba que ha sido ejecutado 4 veces, a través de la vairable times del servicio

El resultado de la ejecución del test anterior produce las siguientes trazas en el Log:

Como vemos, ha intentado ejecutar 5 veces el método, hasta que el método ha devuelto en la 5ª vez un resultado satisfactorio, por lo que ha dejado de reintentar. Si hacemos la prueba de bajar el número de reintentos a 3:

Vemos que se lanza una excepción, capturada en el test.

5. Conclusiones

Spring-retry nos ofrece una solución estándar, sencilla y rápida para la gestión de reintentos en nuestras aplicaciones JEE, proporcionando políticas muy diversas, desde un único reintento hasta la repetición exponencial.
Evita además que tengamos que implementar una política de reintentos para cada aplicación que así lo requiera.

Se puede consultar el proyecto en github aquí

6. Referencias

Dejar respuesta

Please enter your comment!
Please enter your name here