2011-01-19 30 views
20

Tengo una serie de situaciones en las que tengo que volver a intentar una tarea n veces si falla (a veces con alguna forma de lógica de rellamada antes de reintentar). En general, si se lanza una excepción, la tarea debe volver a intentarse hasta el recuento máximo de reintento.Retry Task Framework

Puedo escribir fácilmente algo para hacer esto de manera bastante genérica, pero no queriendo volver a inventar la rueda. Me preguntaba si alguien puede recomendar algún framework para esto. Lo único que he podido encontrar es: Ant Retry, pero no quiero usar tareas Ant directamente en mi aplicación.

Gracias

+0

¿qué tipo de tarea quieres volver a intentar? – fmucar

+5

@fatih - No importa, reintentar una tarea es un concepto abstracto. – Scruffers

+0

Curiosamente, todo el mundo parece envolver la acción para volver a intentar en alguna clase que haga el reintento, lo que requiere que la acción se reintente para ajustarse a alguna interfaz. Prefiero implementar un administrador de estrategias de rebobinado/reintento con solo un método 'boolean shallWeRetryOnceMore()', cuando se lo llama devuelve inmediatamente 'false' o espera un tiempo, dependiendo del algoritmo de restitución, y luego devuelve verdadero. – Harald

Respuesta

15

Puede usar RetriableTasks como se indica en esta publicación: Retrying Operations in Java. Puede fácilmente cambiar su algoritmo de espera si lo desea.

Código de ejemplo:

//creates a task which will retry 3 times with an interval of 5 seconds 
RetriableTask r = new RetriableTask(3, 5000, new Callable(){ 
    public Object call() throws Exception{ 
     //put your code here 
    } 
}); 
r.call(); 
0

Puede utilizar Quartz. Mire this respuesta de desbordamiento de pila.

1

Una opción para factorizar esto fuera de su base de código es usar el patrón de comando entre los componentes de su aplicación. Una vez que convierte una llamada a un método de negocio en un objeto, puede pasar la llamada fácilmente y tener un RetryHandler abstracto que toma un comando y lo reintenta. Esto debería ser independiente de la llamada real y reutilizable.

+0

Gracias Jochen, esto es ampliamente lo que tengo ya que las tareas son instancias ejecutables o invocables de todos modos. Solo esperaba que fuera un marco que proporcionara opciones como especificar diferentes tipos de lógica de reintento y algoritmos de retroceso ... – Scruffers

+0

No es que yo sepa, no. Lo cual es bastante inusual, estaría de acuerdo. Usamos reintentos principalmente para tratar excepciones optimistas cuando hablamos con la capa de persistencia. –

8

Si utiliza primavera:

//import the necessary classes 
import org.springframework.batch.retry.RetryCallback; 
import org.springframework.batch.retry.RetryContext; 
import org.springframework.batch.retry.backoff.ExponentialBackOffPolicy; 
import org.springframework.batch.retry.policy.SimpleRetryPolicy; 
import org.springframework.batch.retry.support.RetryTemplate; 
... 

// create the retry template 
final RetryTemplate template = new RetryTemplate(); 
template.setRetryPolicy(new SimpleRetryPolicy(5)); 
final ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy(); 
backOffPolicy.setInitialInterval(1000L); 
template.setBackOffPolicy(backOffPolicy); 

// execute the operation using the retry template 
template.execute(new RetryCallback<Remote>() { 
    @Override 
    public Remote doWithRetry(final RetryContext context) throws Exception { 
    return (Remote) Naming.lookup("rmi://somehost:2106/MyApp"); 
    } 
}); 

Original blog post

+1

Más específicamente, si usa, y desea una dependencia, Spring Batch. – Jonathan

0

he implementado una utilidad de reintento bastante flexibles here

puede reintentar cualquier rescatable con :

public static <T> T executeWithRetry(final Callable<T> what, final int nrImmediateRetries, 
     final int nrTotalRetries, final int retryWaitMillis, final int timeoutMillis, 
     final Predicate<? super T> retryOnReturnVal, final Predicate<Exception> retryOnException) 

con reintentos inmediatos + diferidos, con un tiempo de espera máximo, y vuelva a intentar en las decisiones basadas en el resultado o la excepción.

Existen otras versiones de esta función con más o menos flexibilidad.

me han escrito también un aspecto que se puede aplicar con anotaciones RetryRetry Aspect

9

Echa un vistazo a Failsafe. Es un simple, biblioteca cero dependencia para realizar reintentos, y soporta reintentos sincrónicas y asincrónicas, Java 8 de integración, detectores de eventos, integración con otras API asíncronas, etc:

RetryPolicy retryPolicy = new RetryPolicy() 
    .retryOn(ConnectException.class, SocketException.class); 
    .withMaxRetries(3); 

Connection connection = Failsafe.with(retryPolicy).get(() -> connect()); 

no se pone mucho más fácil.

0

Tengo una respuesta ya, pero fue hace tres años y tengo que añadir que ahora me encanta el proyecto guava-retrying. NO tiene guayaba como dependencia, pero creo que el autor se inspiró en este proyecto . Déjame mostrarte el código.

Callable<Boolean> callable = new Callable<Boolean>() { 
    public Boolean call() throws Exception { 
     return true; // do something useful here 
    } 
}; 

Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder() 
     .retryIfResult(Predicates.<Boolean>isNull()) 
     .retryIfExceptionOfType(IOException.class) 
     .retryIfRuntimeException() 
     .withStopStrategy(StopStrategies.stopAfterAttempt(3)) 
     .build(); 
try { 
    retryer.call(callable); 
} catch (RetryException e) { 
    e.printStackTrace(); 
} catch (ExecutionException e) { 
    e.printStackTrace(); 
} 
+2

Por supuesto, Guava es una dependencia: https://github.com/rholder/guava-retrying/blob/master/build.gradle#L84 De lo contrario, su ejemplo de código no se compilaría. – Jonathan

+1

Corregido, gracias @Jonathan – mulya

+0

Esto está limpio y la abstracción de StopStrategies es genial. Sin embargo, no estoy seguro de si esto funciona bien (o no) para implementar estrategias de reintento diferentes, como un reintento de cadencia regular en un intervalo de tiempo fijo, o retroceso exponencial, o escalonado. – g7p

4

Si está utilizando primavera, es muy simple usando Spring Retry Biblioteca.

Ahora, Primavera de reintento es una biblioteca individual (anterior que era parte de la primavera lotes) marco.

Paso 1: Agregar spring retry dependency.

<dependency> 
    <groupId>org.springframework.retry</groupId> 
    <artifactId>spring-retry</artifactId> 
    <version>1.1.2.RELEASE</version> 
</dependency> 

Paso 2: Agregar@EnableRetry anotación a su clase que contiene el método main() de la aplicación o en cualquiera de su clase @Configuration.

Paso 3: Añadir@Retryable anotación a su método la que desea volver a intentar/llamar de nuevo, en caso de excepciones.

@Retryable(maxAttempts=5,backoff = @Backoff(delay = 3000)) 
public void retrySomething() throws Exception{ 
    logger.info("printSomething{} is called"); 
    throw new SQLException(); 
} 

Esta anotación @Retryable volverá a intentar/call retrySomething() 5 veces (incluyendo la primera falla).

El hilo actual esperará 3000 ms o 3 segundos entre el próximo intento.