2011-01-19 12 views
20

Ho un numero di situazioni in cui ho bisogno di riprovare un'attività n-volte se fallisce (a volte con qualche forma di logica back-off-before-retry). In genere, se viene generata un'eccezione, l'attività deve essere ritentata fino al conteggio massimo dei tentativi.Retry Task Framework

Posso facilmente scrivere qualcosa per farlo in modo abbastanza generico, ma non volendo re-inventare la ruota mi chiedevo se qualcuno può consigliare qualsiasi framework per questo. L'unica cosa che sono stato in grado di trovare è: Ant Retry ma non voglio usare le attività Ant direttamente nella mia applicazione.

Grazie

+0

che tipo di attività si desidera riprovare? – fmucar

+5

@fatih - Non importa, riprovare un'attività è un concetto astratto. – Scruffers

+0

È interessante notare che tutti sembrano avvolgere l'azione per riprovare in una classe che esegue il tentativo, il che richiede che l'azione venga ritentata per conformarsi a un'interfaccia. Preferirei implementare un gestore di strategia backoff/retry con un solo metodo 'booleano shallWeRetryOnceMore()', quando viene chiamato restituisce immediatamente 'false' o attende un tempo, a seconda dell'algoritmo di backoff, e quindi restituisce true. – Harald

risposta

15

È possibile utilizzare RetriableTasks come descritto in questo post: Retrying Operations in Java. Se lo desideri, puoi facilmente cambiare il suo algoritmo di attesa.

codice di esempio:

//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

È possibile utilizzare Quartz. Guarda this risposta Overflow dello stack.

1

Un'opzione per escludere questo codice dalla base è quella di utilizzare lo schema di comando tra i componenti dell'applicazione. Dopo aver trasformato una chiamata in un metodo aziendale in un oggetto, è possibile gestire facilmente la chiamata e disporre di un RetryHandler astratto che accetta un comando e lo riprova. Questo dovrebbe essere indipendente dall'effettiva chiamata e riutilizzabile.

+0

Grazie Jochen, questo è ampiamente quello che ho dal momento che le attività sono istanze Runnable o Callable comunque. Speravo solo che il loro potesse essere un framework che forniva opzioni come la specificazione di diversi tipi di logica di retry e algoritmi di backoff ... – Scruffers

+0

Non che io ne sia a conoscenza, no. Il che è abbastanza insolito, sarei d'accordo. Usiamo i tentativi principalmente per gestire eccezioni ottimistiche quando si parla al livello di persistenza. –

8

Se si utilizza 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

In particolare, se si utilizza e si desidera una dipendenza, Spring Batch. – Jonathan

0

ho implementato un programma di utilità di tentativi abbastanza flessibile here

è possibile riprovare qualsiasi richiamabile 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 tentativi immediati + ritardati, con un timeout massimo e riprovare in base a decisioni basate su risultato o eccezione.

Esistono diverse altre versioni di questa funzione con maggiore o minore flessibilità.

ho scritto anche un aspetto che può essere applicata con annotazioni RetryRetry Aspect

9

Scopri Failsafe. E 'un semplice, biblioteca zero dipendenza per l'esecuzione di tentativi, e supporta tentativi sincrona e asincrona, Java 8 di integrazione, ascoltatori di eventi, l'integrazione con altre API asincrone, ecc:

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

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

non ottiene molto più facile.

0

Ho già una risposta, ma era tre anni fa e devo aggiungere che ora assolutamente amo il progetto guava-retrying. NON ha Guava come dipendenza, ma penso che l'autore sia stato ispirato da questo progetto . Lascia che ti mostri il codice.

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

Ovviamente Guava è una dipendenza: https://github.com/rholder/guava-retrying/blob/master/build.gradle#L84 Altrimenti il ​​tuo esempio di codice non verrà compilato. – Jonathan

+1

Risolto, grazie @Jonathan – mulya

+0

Questo è pulito e l'astrazione di StopStrategie è interessante. Tuttavia, non sono chiaro se questo funziona bene (o del tutto) per l'implementazione di diverse strategie di ripetizione come un normale tentativo di cadenza in un intervallo di tempo fisso, o un back-off esponenziale o scaglionato. – g7p

4

Se si utilizza Primavera, è molto semplice utilizzando Spring Retry Biblioteca.

Ora, Primavera Riprova è una libreria individuale (in precedenza faceva parte di primavera Batch) quadro.

Punto 1: Aggiungere la dipendenza del numero di ripetizioni della molla.

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

Step2: Aggiungi @EnableRetry annotazione alla classe che contiene il metodo main() dell'applicazione o in una delle vostra classe @Configuration.

Fase 3: Aggiungi @Retryable annotazione al tuo metodo di cui si desidera riprovare/richiamare, in caso di eccezioni.

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

Questa @Retryable annotazione ritenterà/chiamare retrySomething() 5 volte (compresa il primo fallimento).

Il thread corrente attenderà 3000 ms o 3 secondi tra il prossimo tentativo.