Spring Retry es un módulo del framework Spring que facilita la reejecución de operaciones que han fallado temporalmente.
Casos de Uso Comunes
- Interacciones de Red y Llamadas Remotas: Manejo de fallos transitorios en comunicaicones de red o llamadas a servicios externos debido a inestabilidad o indisponibilidad temporal.
- Operaciones con Bases de Datos: Recuperación ante problemas momentáneos al interactuar con bases de datos, como bloqueos o desconexiones.
- Dependencias Externas: Gestión de fallos en servicios, dispositivos o sistemas externos que pueden presentar fallos ocasionales, asegurando la resiliencia de la aplicación.
- Control de Concurrencia: Mitigación de errores debidos a condiciones de carrera o problemas de concurrencia en entornos multihilo.
- Lógica de Negocio Compleja: Cuando ciertas partes de la lógica de negocio pueden requerir múltiples intentos para completarse con éxito.
Generalmente, las operaciones que se benefician de reintentos son aquellas que experimentan fallos "instantáneos" o transitorios, donde un nuevo intento tiene una alta probabilidad de éxito.
Implementación
1. Añadir la Dependencia (Maven)
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.3.4</version>>
</dependency>
2. Habilitar Spring Retry
Añada la anotación @EnableRetry a su clase de configuración principal.
@Configuration
@EnableRetry
public class AppConfig {
// ... otros beans y configuración
}
3. Utilizar la Anotación @Retryable
Marque los métodos que desea que sean reintentables con @Retryable.
@Retryable
public void metodoConReintentos() {
System.out.println("Ejecutando método con reintentos en: " + System.currentTimeMillis());
throw new RuntimeException("Error simulado para reintento.");
}
Por defecto, este método se reintentará hasta 3 veces con un intervalo de 1 segundo entre intentos.
Detalles de la Anotación @Retryable (v1.3.4)
maxAttempts: Número máximo de intentos (incluyendo el primer intento fallido). Por defecto: 3.maxAttemptsExpression: Expresión SpEL para calcular el número máximo de intentos.value/include: Clases de excepciones que deben ser reintentables.exclude: Clases de excepciones que NO deben ser reintentables.backoff: Configura la política de espera (backoff) entre reintentos.recover: Nombre del método en una clase recuperadora anotado con@Recoverque se llamará si todos los reintentos fallan.interceptor: Nombre de un bean interceptor personalizado. Si se usa, los demás atributos se ignoran.label: Clave utilizada para el estado en reintentos con estado (stateful).stateful: Indica si el reintento debe mantener estado entre llamadas sucesivas con los mismos parámetros. Si esfalse, las excepciones reintentables no se vuelven a lanzar.exceptionExpression: Expresión SpEL para determinar condicionalmente si se debe reintentar después de queSimpleRetryPolicy.canRetry()retornetrue. Evalúa el últimoThrowable.
Detalles de la Anotación @Backoff
value: Tiempo de espera en milisegundos (usado sidelayno está especificado). Por defecto: 1000.delay: Tiempo de espera inicial en milisegundos. Usado como base para el cálculo en políticas exponenciales y como mínimo en políticas uniformes.maxDelay: Tiempo máximo de espera en milisegundos entre reintentos.multiplier: Factor de multiplicación para calcular el siguiente tiempo de espera en políticas exponenciales. Debe ser positivo. Por defecto: 0 (ignorado).delayExpression: Expresión SpEL para calcular el tiempo de espera inicial.maxDelayExpression: Expresión SpEL para calcular el tiempo máximo de espera.multiplierExpression: Expresión SpEL para calcular el multiplicador.random: Si estrueymultiplieres positivo, el tiempo de espera se aleatoriza dentro de un rango (multiplicador).randomExpression: Expresión SpEL para calcular el valor derandom.
Las expresiones SpEL pueden referenciar parámetros del método, valores de retorno, y otros beans contextuales (usando #root para el objeto principal evaluado).
4. Anotación @Recover
Defina un método recuperador para manejar la situación cuando todos los reintentos fallan. Este método debe tener la misma firma que el método reintentable, pero aceptando un Throwable como parámetro.
@Recover
public void fallback(Throwable cause) {
System.err.println("Todos los reintentos fallaron. Causa: " + cause.getMessage());
// Lógica para manejar el fallo final, como registrar el error o notificar.
}
5. Uso de RetryTemplate
Para un control más granular, puede configurar y usar RetryTemplate directamente.
@Bean
public RetryTemplate retryTemplate() {
RetryTemplate template = new RetryTemplate();
// Configuración de la política de espera (BackOffPolicy)
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
backOffPolicy.setInitialInterval(500L); // Intervalo inicial de 500ms
backOffPolicy.setMultiplier(2.0); // Duplicar el intervalo en cada reintento
backOffPolicy.setMaxInterval(10000L); // Intervalo máximo de 10 segundos
template.setBackOffPolicy(backOffPolicy);
// Configuración de la política de reintentos (RetryPolicy)
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(5); // Intentar hasta 5 veces
template.setRetryPolicy(retryPolicy);
// Opcionalmente, configurar listeners
// template.setListeners(new RetryListener[]{...});
return template;
}
Uso del template:
@Autowired
private RetryTemplate retryTemplate;
public void ejecutarConTemplate() {
try {
String resultado = retryTemplate.execute(context -> {
System.out.println("Intentando operación dentro de RetryTemplate. Intento: " + context.getRetryCount());
// Lógica de negocio que puede fallar
return realizarOperacionCritica();
});
System.out.println("Operación completada exitosamente: " + resultado);
} catch (Throwable e) {
System.err.println("La operación falló después de todos los reintentos.");
// Manejo final del fallo
}
}
private String realizarOperacionCritica() {
// Simular una operación que puede fallar
if (Math.random() < 0.8) { // 80% de probabilidad de fallo
throw new RuntimeException("Fallo simulado en la operación crítica.");
}
return "Éxito en la operación crítica";
}
Funcionamiento Interno
Spring Retry opera a través de varios componentes clave:
RetryTemplate: El orquestador central. Ejecuta el código proporcionado a través de unRetryCallbacky aplica las políticas de reintento y espera configuradas.RetryPolicy: Define las condiciones bajo las cuales se deben realizar los reintentos (p. ej., número máximo de intentos, tipos de excepciones). Implementaciones comunes incluyenSimpleRetryPolicyyCircuitBreakerRetryPolicy.BackOffPolicy: Controla el tiempo de espera entre intentos de reintento. Implementaciones comoFixedBackOffPolicyyExponentialBackOffPolicyofrecen diferentes estrategias de espera.RetryListener: Permite interceptar y reaccionar a eventos durante el proceso de reintento (antes de cada intento, después de un fallo, etc.).RetryContext: Objeto que encapsula el estado actual del proceso de reintento, como el número de intentos realizados y la última excepción ocurrida. Es utilizado por las políticas para tomar decisiones.BackOffContext: Similar aRetryContextpero específico para la gestión del estado de la política de espera.RetryCallback: Interfaz funcional que contiene la lógica de negocio que se ejecutará y que podría necesitar ser reintentada.RecoveryCallback: Interfaz funcional que contiene la lógica a ejecutar si todos los intentos de reintento fallan.
El flujo general implica que RetryTemplate invoca repetidamente el RetryCallback. Después de cada fallo, la RetryPolicy decide si se debe continuar, y si es así, la BackOffPolicy determina cuánto tiempo esperar antes del siguiente intento.