Comment implémentez-vous une nouvelle tentative de capture?

203

Try-catch est destiné à aider à la gestion des exceptions. Cela signifie en quelque sorte que cela aidera notre système à être plus robuste: essayez de récupérer après un événement inattendu.

Nous soupçonnons que quelque chose pourrait se produire lors de l'exécution et de l'instruction (envoi d'un message), il est donc inclus dans l'essai. Si quelque chose de presque inattendu se produit, nous pouvons faire quelque chose: nous écrivons la capture. Je ne pense pas que nous ayons appelé pour simplement enregistrer l'exception. Je pense que le bloc catch est destiné à nous donner la possibilité de récupérer de l'erreur.

Supposons maintenant que nous nous remettions de l'erreur parce que nous pouvions corriger ce qui n'allait pas. Ça pourrait être super sympa de refaire un essai:

try{ some_instruction(); }
catch (NearlyUnexpectedException e){
   fix_the_problem();
   retry;
}

Cela tomberait rapidement dans la boucle éternelle, mais disons que le fix_the_problem renvoie true, puis nous réessayons. Étant donné qu'il n'y a rien de tel en Java, comment résoudriez-vous ce problème? Quel serait votre meilleur code de conception pour résoudre ce problème?

C'est comme une question philosophique, étant donné que je sais déjà que ce que je demande n'est pas directement pris en charge par Java.

Andres Farias
la source
5
Quel genre d'exception est-ce?
Bhesh Gurung
23
J'aime bien le nom de votre exception. ;)
Rohit Jain
En fait, il n'y a pas beaucoup d'exceptions dont vous pouvez vous remettre. J'avoue que ma motivation initiale n'était pas une vraie exception, mais moyen d'éviter un si cela ne se produira presque jamais: j'essaye de remove()partir d'un java.util.Queue, qui va et InvalidElementExceptionquand la file d'attente est vide. Au lieu de demander s'il est vide, je contourne les actions dans un try-catch (qui, sous concurrence, devient obligatoire même avec le précédent if). Dans un tel cas, dans le catchbloc, je demanderais de remplir la file d'attente avec plus d'éléments, puis de réessayer. Voila.
Andres Farias
1
Je peux voir que la manière habituelle de le faire serait pour l'accès à la base de données, si la connexion a échoué à se reconnecter, si elle échoue, puis lancez une exception majeure sinon réessayez l'appel. Comme cela a été dit, nous pourrions le faire en boucle avec une vérification en bas si (erreur <> 0) puis revenir en arrière sinon casser;
Theresa Forster

Réponses:

305

Vous devez enfermer votre try-catchintérieur dans une whileboucle comme celle-ci: -

int count = 0;
int maxTries = 3;
while(true) {
    try {
        // Some Code
        // break out of loop, or return, on success
    } catch (SomeException e) {
        // handle exception
        if (++count == maxTries) throw e;
    }
}

J'ai pris countet maxTriespour éviter de courir dans une boucle infinie, au cas où l'exception continue de se produire dans votre try block.

Rohit Jain
la source
3
J'ai pensé à quelque chose comme ça au début, sans les maxTries. Merci d'avoir répondu!
Andres Farias
6
@AndresFarias .. Oui, le point le plus important de cette réponse est d'inclure a maxTries. Sinon, il s'exécutera infinite loopsi un utilisateur donne continuellement une mauvaise entrée et ne sortira donc pas. Vous êtes bienvenu cependant. :)
Rohit Jain
merci pour cela - cela m'a juste évité d'avoir à écrire du code très narquois!
David Holiday
2
Est-il possible d'ajouter la fonction Thread.sleep () à l'intérieur du catch ici. Parce que dans certains cas, comme attendre la réponse de la page dans la bibliothèque Selenium qui est devenue critique. Merci.
Suat Atan PhD
2
Fonctionne très bien! Pour les débutants: si vous obtenez une boucle infinie positive, vérifiez si vous avez ajouté "break;" à la fin dans le bloc "try".
Krzysztof Walczewski
59

Solution "entreprise" obligatoire:

public abstract class Operation {
    abstract public void doIt();
    public void handleException(Exception cause) {
        //default impl: do nothing, log the exception, etc.
    }
}

public class OperationHelper {
    public static void doWithRetry(int maxAttempts, Operation operation) {
        for (int count = 0; count < maxAttempts; count++) {
            try {
                operation.doIt();
                count = maxAttempts; //don't retry
            } catch (Exception e) {
                operation.handleException(e);
            }
        }
    }
}

Et pour appeler:

OperationHelper.doWithRetry(5, new Operation() {
    @Override public void doIt() {
        //do some stuff
    }
    @Override public void handleException(Exception cause) {
        //recover from the Exception
    }
});
ach
la source
6
Vous devriez relancer l'exception si la dernière nouvelle tentative échoue, comme cela est fait dans les autres réponses données.
cvacca
35

Comme d'habitude, la meilleure conception dépend des circonstances particulières. Habituellement cependant, j'écris quelque chose comme:

for (int retries = 0;; retries++) {
    try {
        return doSomething();
    } catch (SomeException e) {
        if (retries < 6) {
            continue;
        } else {
            throw e;
        }
    }
}
meriton
la source
Attendez, pourquoi ne pas avoir la condition dans la déclaration de boucle for comme: for (int retries = 0; retries <6; retries ++) ??
Didier A.
8
Parce que je veux seulement lancer la dernière tentative, et donc le bloc catch a besoin de cette condition, ce qui rend la condition dans le for redondante.
meriton
1
Je ne pense pas que cela continuesoit nécessaire là-bas .. Et vous pouvez simplement inverser la condition if.
Koray Tugay
19

Bien que try/catchdans whileest bien connu et une bonne stratégie que je veux vous suggérer appel récursif:

void retry(int i, int limit) {
    try {

    } catch (SomeException e) {
        // handle exception
        if (i >= limit) {
            throw e;  // variant: wrap the exception, e.g. throw new RuntimeException(e);
        }
        retry(i++, limit);
    }
}
AlexR
la source
41
Comment la récursivité est-elle meilleure qu'une boucle pour ce cas d'utilisation?
Dan
7
La trace de la pile peut sembler un peu étrange sur celle-ci, car ne compterait-elle pas limitla méthode récursive? Par opposition à la version en boucle, qui jettera au niveau 'original' ...
Clockwork-Muse
7
Bien sûr, il a l'air élégant sur le papier, mais je ne suis pas sûr que la récursivité soit la bonne approche.
Thomas
3
Je ne comprends pas pourquoi la récursion ici aussi. Quoi qu'il en soit, je pense que cela pourrait être simplifié pour:void retry(int times) { (...) if (times==0) throw w; retry(times--);
sinuhepop
8
Il est peu pratique d'utiliser la récursivité comme substitut à une simple itération. La récursivité est à utiliser lorsque vous souhaitez pousser et afficher certaines données.
Marquis de Lorne du
19

Votre scénario exact géré via Failsafe :

RetryPolicy retryPolicy = new RetryPolicy()
  .retryOn(NearlyUnexpectedException.class);

Failsafe.with(retryPolicy)
  .onRetry((r, f) -> fix_the_problem())
  .run(() -> some_instruction());

Assez simple.

Jonathan
la source
5
très belle bibliothèque.
Maksim
pour ceux qui se demandent, vous en aurez besoin dans vos dépendances Gradle - compilez 'net.jodah: Failuresafe: 1.1.0'
Shreyas
18

Vous pouvez utiliser les annotations AOP et Java depuis jcabi-aspects (je suis développeur):

@RetryOnFailure(attempts = 3, delay = 5)
public String load(URL url) {
  return url.openConnection().getContent();
}

Vous pouvez également utiliser @Loggableet @LogExceptionannotations.

yegor256
la source
Hou la la ! Sonne fantaisie! :)
Alind Billore
Devrait être la meilleure réponse.
Mohamed Taher Alrefaie
2
Existe-t-il un moyen de "corriger" l'erreur en cas d'échec de la tentative (certaines adoptions peuvent-elles corriger la prochaine tentative)? voir la question: fix_the_problem();dans le bloc catch
warch
Étant donné le nombre de problèmes ouverts et le temps écoulé pour que les bogues reconnus ne soient pas corrigés, je ne compterais pas sur cette bibliothèque.
Michael Lihs
6

La plupart de ces réponses sont essentiellement les mêmes. Le mien l'est aussi, mais c'est la forme que j'aime

boolean completed = false;
Throwable lastException = null;
for (int tryCount=0; tryCount < config.MAX_SOME_OPERATION_RETRIES; tryCount++)
{
    try {
        completed = some_operation();
        break;
    }
    catch (UnlikelyException e) {
        lastException = e;
        fix_the_problem();
    }
}
if (!completed) {
    reportError(lastException);
}
Stephen P
la source
Un inconvénient est que vous appelez également fix_the_problemaprès la dernière tentative. Cela pourrait être une opération coûteuse et pourrait perdre du temps.
Joachim Sauer
2
@JoachimSauer True. Vous pourriez if (tryCount < max) fix()- mais c'est le format d'une approche générale; les détails dépendraient d'un cas spécifique. Il y a aussi un Retryer à base de goyave que j'ai regardé.
Stephen P
4

Spring AOP et solution basée sur les annotations:

Utilisation ( @RetryOperationest notre annotation personnalisée pour le travail):

@RetryOperation(retryCount = 1, waitSeconds = 10)
boolean someMethod() throws Exception {
}

Nous aurons besoin de deux choses pour y parvenir: 1. une interface d'annotation, et 2. un aspect ressort. Voici une façon de les implémenter:

L'interface d'annotation:

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RetryOperation {
    int retryCount();
    int waitSeconds();
}

L'aspect printanier:

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;

@Aspect @Component 
public class RetryAspect {

    private static final Logger LOGGER = LoggerFactory.getLogger(RetryAspect.class);

    @Around(value = "@annotation(RetryOperation)")
    public Object retryOperation(ProceedingJoinPoint joinPoint) throws Throwable {

        Object response = null;
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        RetryOperation annotation = method.getAnnotation(RetryOperation.class);
        int retryCount = annotation.retryCount();
        int waitSeconds = annotation.waitSeconds();
        boolean successful = false;

        do {
            try {
                response = joinPoint.proceed();
                successful = true;
            } catch (Exception ex) {
                LOGGER.info("Operation failed, retries remaining: {}", retryCount);
                retryCount--;
                if (retryCount < 0) {
                    throw ex;
                }
                if (waitSeconds > 0) {
                    LOGGER.info("Waiting for {} second(s) before next retry", waitSeconds);
                    Thread.sleep(waitSeconds * 1000l);
                }
            }
        } while (!successful);

        return response;
    }
}
Vivek Sethi
la source
3

Utilisez une whileboucle avec le statusdrapeau local . Initialisez l'indicateur as falseet réglez-le truelorsque l'opération réussit, par exemple ci-dessous:

  boolean success  = false;
  while(!success){
     try{ 
         some_instruction(); 
         success = true;
     } catch (NearlyUnexpectedException e){
       fix_the_problem();
     }
  }

Cela continuera à réessayer jusqu'à son succès.

Si vous ne souhaitez réessayer qu'un certain nombre de fois, utilisez également un compteur:

  boolean success  = false;
  int count = 0, MAX_TRIES = 10;
  while(!success && count++ < MAX_TRIES){
     try{ 
         some_instruction(); 
         success = true;
     } catch (NearlyUnexpectedException e){
       fix_the_problem();
     }
  }
  if(!success){
    //It wasn't successful after 10 retries
  }

Cela tentera au maximum 10 fois en cas d'échec jusqu'à ce qu'un se termine si c'est réussi avant la main.

Yogendra Singh
la source
Plutôt que de vérifier !successvotre temps, vous pouvez simplement vous évader lorsque le succès est vrai.
Rohit Jain
1
@RohitJain: Cela me semble plus propre.
Yogendra Singh
@YogendraSingh .. Étrange. que vous ne modifiez pas votre successn'importe où dans votre catch. Il semble donc superflu de le vérifier, à chaque exécution de catch.
Rohit Jain
@RohitJain: Catch corrige simplement les données. Il reviendra et exécutera à nouveau l'instruction. En cas de succès, il modifiera le success. Essaye le.
Yogendra Singh
3

C'est une vieille question mais une solution est toujours d'actualité. Voici ma solution générique en Java 8 sans utiliser de bibliothèque tierce:

public interface RetryConsumer<T> {
    T evaluate() throws Throwable;
}
public interface RetryPredicate<T> {
    boolean shouldRetry(T t);
}
public class RetryOperation<T> {
    private RetryConsumer<T> retryConsumer;
    private int noOfRetry;
    private int delayInterval;
    private TimeUnit timeUnit;
    private RetryPredicate<T> retryPredicate;
    private List<Class<? extends Throwable>> exceptionList;

    public static class OperationBuilder<T> {
        private RetryConsumer<T> iRetryConsumer;
        private int iNoOfRetry;
        private int iDelayInterval;
        private TimeUnit iTimeUnit;
        private RetryPredicate<T> iRetryPredicate;
        private Class<? extends Throwable>[] exceptionClasses;

        private OperationBuilder() {
        }

        public OperationBuilder<T> retryConsumer(final RetryConsumer<T> retryConsumer) {
            this.iRetryConsumer = retryConsumer;
            return this;
        }

        public OperationBuilder<T> noOfRetry(final int noOfRetry) {
            this.iNoOfRetry = noOfRetry;
            return this;
        }

        public OperationBuilder<T> delayInterval(final int delayInterval, final TimeUnit timeUnit) {
            this.iDelayInterval = delayInterval;
            this.iTimeUnit = timeUnit;
            return this;
        }

        public OperationBuilder<T> retryPredicate(final RetryPredicate<T> retryPredicate) {
            this.iRetryPredicate = retryPredicate;
            return this;
        }

        @SafeVarargs
        public final OperationBuilder<T> retryOn(final Class<? extends Throwable>... exceptionClasses) {
            this.exceptionClasses = exceptionClasses;
            return this;
        }

        public RetryOperation<T> build() {
            if (Objects.isNull(iRetryConsumer)) {
                throw new RuntimeException("'#retryConsumer:RetryConsumer<T>' not set");
            }

            List<Class<? extends Throwable>> exceptionList = new ArrayList<>();
            if (Objects.nonNull(exceptionClasses) && exceptionClasses.length > 0) {
                exceptionList = Arrays.asList(exceptionClasses);
            }
            iNoOfRetry = iNoOfRetry == 0 ? 1 : 0;
            iTimeUnit = Objects.isNull(iTimeUnit) ? TimeUnit.MILLISECONDS : iTimeUnit;
            return new RetryOperation<>(iRetryConsumer, iNoOfRetry, iDelayInterval, iTimeUnit, iRetryPredicate, exceptionList);
        }
    }

    public static <T> OperationBuilder<T> newBuilder() {
        return new OperationBuilder<>();
    }

    private RetryOperation(RetryConsumer<T> retryConsumer, int noOfRetry, int delayInterval, TimeUnit timeUnit,
                           RetryPredicate<T> retryPredicate, List<Class<? extends Throwable>> exceptionList) {
        this.retryConsumer = retryConsumer;
        this.noOfRetry = noOfRetry;
        this.delayInterval = delayInterval;
        this.timeUnit = timeUnit;
        this.retryPredicate = retryPredicate;
        this.exceptionList = exceptionList;
    }

    public T retry() throws Throwable {
        T result = null;
        int retries = 0;
        while (retries < noOfRetry) {
            try {
                result = retryConsumer.evaluate();
                if (Objects.nonNull(retryPredicate)) {
                    boolean shouldItRetry = retryPredicate.shouldRetry(result);
                    if (shouldItRetry) {
                        retries = increaseRetryCountAndSleep(retries);
                    } else {
                        return result;
                    }
                } else {
                    // no retry condition defined, no exception thrown. This is the desired result.
                    return result;
                }
            } catch (Throwable e) {
                retries = handleException(retries, e);
            }
        }
        return result;
    }

    private int handleException(int retries, Throwable e) throws Throwable {
        if (exceptionList.contains(e.getClass()) || (exceptionList.isEmpty())) {
            // exception is excepted, continue retry.
            retries = increaseRetryCountAndSleep(retries);
            if (retries == noOfRetry) {
                // evaluation is throwing exception, no more retry left. Throw it.
                throw e;
            }
        } else {
            // unexpected exception, no retry required. Throw it.
            throw e;
        }
        return retries;
    }

    private int increaseRetryCountAndSleep(int retries) {
        retries++;
        if (retries < noOfRetry && delayInterval > 0) {
            try {
                timeUnit.sleep(delayInterval);
            } catch (InterruptedException ignore) {
                Thread.currentThread().interrupt();
            }
        }
        return retries;
    }
}

Ayons un cas de test comme:

@Test
public void withPredicateAndException() {
    AtomicInteger integer = new AtomicInteger();
    try {
        Integer result = RetryOperation.<Integer>newBuilder()
                .retryConsumer(() -> {
                    int i = integer.incrementAndGet();
                    if (i % 2 == 1) {
                        throw new NumberFormatException("Very odd exception");
                    } else {
                        return i;
                    }
                })
                .noOfRetry(10)
                .delayInterval(10, TimeUnit.MILLISECONDS)
                .retryPredicate(value -> value <= 6)
                .retryOn(NumberFormatException.class, EOFException.class)
                .build()
                .retry();
        Assert.assertEquals(8, result.intValue());
    } catch (Throwable throwable) {
        Assert.fail();
    }
}
GirishB
la source
belle idée, un constructeur pour ça!
HankTheTank
2

Un moyen simple de résoudre le problème serait d'enrouler le try / catch dans une boucle while et de maintenir un décompte. De cette façon, vous pouvez empêcher une boucle infinie en vérifiant un nombre par rapport à une autre variable tout en conservant un journal de vos échecs. Ce n'est pas la solution la plus exquise, mais cela fonctionnerait.

Jordan Kaye
la source
1

Utilisez un do-while pour concevoir un bloc de réessai.

boolean successful = false;
int maxTries = 3;
do{
  try {
    something();
    success = true;
  } catch(Me ifUCan) {
    maxTries--;
  }
} while (!successful || maxTries > 0)
Rahul Malhotra
la source
2
Le code devrait
lever l'
1

Au cas où cela serait utile, quelques options supplémentaires à considérer, toutes regroupées (fichier d'arrêt au lieu de nouvelles tentatives, mise en veille, continuer la boucle plus grande), toutes éventuellement utiles.

 bigLoop:
 while(!stopFileExists()) {
    try {
      // do work
      break;
    }
    catch (ExpectedExceptionType e) {

       // could sleep in here, too.

       // another option would be to "restart" some bigger loop, like
       continue bigLoop;
    }
    // ... more work
}
rogerdpack
la source
Votants, veuillez laisser des commentaires expliquant pourquoi, merci!
rogerdpack
1
C'est une pure ignorance de voter contre et non de citer une raison.
xploreraj
dormir là n'est pas évident car la boucle while n'attendrait pas
João Pimentel Ferreira
1

Vous pouvez utiliser https://github.com/bnsd55/RetryCatch

Exemple:

RetryCatch retryCatchSyncRunnable = new RetryCatch();
        retryCatchSyncRunnable
                // For infinite retry times, just remove this row
                .retryCount(3)
                // For retrying on all exceptions, just remove this row
                .retryOn(ArithmeticException.class, IndexOutOfBoundsException.class)
                .onSuccess(() -> System.out.println("Success, There is no result because this is a runnable."))
                .onRetry((retryCount, e) -> System.out.println("Retry count: " + retryCount + ", Exception message: " + e.getMessage()))
                .onFailure(e -> System.out.println("Failure: Exception message: " + e.getMessage()))
                .run(new ExampleRunnable());

Au lieu de cela, new ExampleRunnable()vous pouvez transmettre votre propre fonction anonyme.

bnsd55
la source
1

Si toutes les exceptions ne justifient pas une nouvelle tentative, seulement certaines. Et si au moins un essai doit être fait, voici une méthode utilitaire alternative:

void runWithRetry(Runnable runnable, Class<Exception> exClass, int maxRetries) {
        Exception err = null;
        do {
            maxRetries--;
            try {
                runnable.run();
                err = null;
            } catch (Exception e) {
                if(exClass.isAssignableFrom(e.getClass())){
                    err = e;
                }else {
                    throw e;
                }
            }
        } while (err != null && maxRetries > 0);

        if (err != null) {
            throw err;
        }
    }

Usage:

    runWithRetry(() -> {
       // do something
    }, TimeoutException.class, 5)
Dakota du Sud
la source
0

Tout ce que fait Try-Catch est de permettre à votre programme d'échouer sans problème. Dans une instruction catch, vous essayez généralement de consigner l'erreur, et peut-être d'annuler les modifications si vous en avez besoin.

bool finished = false;

while(finished == false)
{
    try
    {
        //your code here
        finished = true
    }
    catch(exception ex)
    {
        log.error("there was an error, ex");
    }
}
Sam je suis dit réintégrer Monica
la source
voulez-vous dire par opposition à (!finished)?
Sam je suis dit Reinstate Monica
1
@RohitJain ça ressemble trop while(finished). Je préfère utiliser la version plus verbeuse.
Sam je suis dit Reinstate Monica
3
À quoi while(!finished)ressemble la terre while (finished)??
Rohit Jain
@Rohit Parce que ce n'est qu'un seul caractère différent. Ils sont tous compilés pour la même chose. En C #, j'utilise une méthode d'extension de chaîne IsPopulated()qui revient juste !IsNullOrEmpty()pour s'assurer que mon intention est comprise par tous les développeurs.
Michael Blackburn
0

Je sais qu'il y a déjà beaucoup de réponses similaires ici, et la mienne n'est pas très différente, mais je la publierai quand même car elle traite d'un cas / problème spécifique.

Lorsque vous traitez avec facebook Graph APIin, PHPvous obtenez parfois une erreur, mais réessayer immédiatement la même chose donnera un résultat positif (pour diverses raisons magiques sur Internet qui dépassent le cadre de cette question). Dans ce cas, il n'est pas nécessaire de corriger une erreur, mais simplement de réessayer car il y a eu une sorte d '"erreur facebook".

Ce code est utilisé immédiatement après la création d'une session facebook:

//try more than once because sometimes "facebook error"
$attempt = 3;
while($attempt-- > 0)
{
    // To validate the session:
    try 
    {
        $facebook_session->validate();
        $attempt = 0;
    } 
    catch (Facebook\FacebookRequestException $ex)
    {
        // Session not valid, Graph API returned an exception with the reason.
        if($attempt <= 0){ echo $ex->getMessage(); }
    } 
    catch (\Exception $ex) 
    {
        // Graph API returned info, but it may mismatch the current app or have expired.
        if($attempt <= 0){ echo $ex->getMessage(); }
    }
}

De plus, en ayant le forcompte à rebours à zéro ( $attempt--), il est assez facile de changer le nombre de tentatives à l'avenir.

KnightHawk
la source
0

voici ma solution avec une approche très simple!

               while (true) {
                    try {
                        /// Statement what may cause an error;
                        break;
                    } catch (Exception e) {

                    }
                }
David Kayo
la source
1
veuillez regarder la réponse @Rohit Jain qui est plus spécifique et non une boucle infinie dans les cas négatifs.
Chandra Shekhar
0

Je ne sais pas si c'est la façon "professionnelle" de le faire et je ne suis pas tout à fait sûr si cela fonctionne pour tout.

boolean gotError = false;

do {
    try {
        // Code You're Trying
    } catch ( FileNotFoundException ex ) {
        // Exception
        gotError = true;
    }
} while ( gotError = true );
Josh
la source
0

Voici une approche réutilisable et plus générique pour Java 8+ qui ne nécessite pas de bibliothèques externes:

public interface IUnreliable<T extends Exception>
{
    void tryRun ( ) throws T;
}

public static <T extends Exception> void retry (int retryCount, IUnreliable<T> runnable) throws T {
    for (int retries = 0;; retries++) {
        try {
            runnable.tryRun();
            return;
        } catch (Exception e) {
            if (retries < retryCount) {
                continue;
            } else {
                throw e;
            }
        }
    }
}

Usage:

@Test
public void demo() throws IOException {
    retry(3, () -> {
        new File("/tmp/test.txt").createNewFile();
    });
}
Jonas_Hess
la source
0

Le problème avec les solutions restantes est que la fonction correspondante essaie en continu sans intervalle de temps, inondant ainsi la pile.

Pourquoi ne pas n'ingérertry que toutes les secondes et éternellement ?

Voici une solution utilisant setTimeoutet une fonction récursive:

(function(){
  try{
    Run(); //tries for the 1st time, but Run() as function is not yet defined
  }
  catch(e){
    (function retry(){
      setTimeout(function(){
        try{
          console.log("trying...");
          Run();
          console.log("success!");
        }
        catch(e){
          retry(); //calls recursively
        }
      }, 1000); //tries every second
    }());
  }
})();



//after 5 seconds, defines Run as a global function
var Run;
setTimeout(function(){
  Run = function(){};
}, 5000);

Remplacez la fonction Run()par la fonction ou le code que vous souhaitez voir trychaque seconde.

João Pimentel Ferreira
la source
0

Essayez-le en utilisant l'annotation springs @Retryable, la méthode ci-dessous réessayera pendant 3 tentatives lorsque RuntimeException se produit

@Retryable(maxAttempts=3,value= {RuntimeException.class},backoff = @Backoff(delay = 500))
public void checkSpringRetry(String str) {
    if(StringUtils.equalsIgnoreCase(str, "R")) {
        LOGGER.info("Inside retry.....!!");
        throw new RuntimeException();
    }
}
NKR
la source
0

Sous l'extrait, exécutez un extrait de code. Si vous obtenez une erreur lors de l'exécution de l'extrait de code, veillez pendant M millisecondes et réessayez. Lien de référence .

public void retryAndExecuteErrorProneCode(int noOfTimesToRetry, CodeSnippet codeSnippet, int sleepTimeInMillis)
  throws InterruptedException {

 int currentExecutionCount = 0;
 boolean codeExecuted = false;

 while (currentExecutionCount < noOfTimesToRetry) {
  try {
   codeSnippet.errorProneCode();
   System.out.println("Code executed successfully!!!!");
   codeExecuted = true;
   break;
  } catch (Exception e) {
   // Retry after 100 milliseconds
   TimeUnit.MILLISECONDS.sleep(sleepTimeInMillis);
   System.out.println(e.getMessage());
  } finally {
   currentExecutionCount++;
  }
 }

 if (!codeExecuted)
  throw new RuntimeException("Can't execute the code within given retries : " + noOfTimesToRetry);
}
Hari Krishna
la source
0

Voici ma solution similaire à d'autres qui peuvent encapsuler une fonction, mais vous permet d'obtenir la valeur de retour des fonctions, si elle réussit.

    /**
     * Wraps a function with retry logic allowing exceptions to be caught and retires made.
     *
     * @param function the function to retry
     * @param maxRetries maximum number of retires before failing
     * @param delay time to wait between each retry
     * @param allowedExceptionTypes exception types where if caught a retry will be performed
     * @param <V> return type of the function
     * @return the value returned by the function if successful
     * @throws Exception Either an unexpected exception from the function or a {@link RuntimeException} if maxRetries is exceeded
     */
    @SafeVarargs
    public static <V> V runWithRetriesAndDelay(Callable<V> function, int maxRetries, Duration delay, Class<? extends Exception>... allowedExceptionTypes) throws Exception {
        final Set<Class<? extends Exception>> exceptions = new HashSet<>(Arrays.asList(allowedExceptionTypes));
        for(int i = 1; i <= maxRetries; i++) {
            try {
                return function.call();
            } catch (Exception e) {
                if(exceptions.contains(e.getClass())){
                    // An exception of an expected type
                    System.out.println("Attempt [" + i + "/" + maxRetries + "] Caught exception [" + e.getClass() + "]");
                    // Pause for the delay time
                    Thread.sleep(delay.toMillis());
                }else {
                    // An unexpected exception type
                    throw e;
                }
            }
        }
        throw new RuntimeException(maxRetries + " retries exceeded");
    }
James Mudd
la source