it-swarm.com.de

Wie implementiere ich einen Re-Try-Catch?

Try-Catch soll bei der Ausnahmebehandlung helfen. Dies bedeutet in gewisser Weise, dass unser System robuster wird: Versuchen Sie, sich von einem unerwarteten Ereignis zu erholen. 

Wir vermuten, dass bei der Ausführung und Anweisung (Senden einer Nachricht) etwas passieren kann, so dass es im Versuch eingeschlossen wird. Wenn so etwas fast Unerwartetes passiert, können wir etwas tun: Wir schreiben den Haken. Ich glaube nicht, dass wir angerufen haben, um die Ausnahme zu protokollieren. Ich glaube, der catch-Block soll uns die Gelegenheit geben, den Fehler zu beheben.

Nehmen wir an, wir beheben den Fehler, weil wir das korrigieren könnten, was falsch war. Es könnte super sein, es noch einmal zu versuchen:

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

Dies würde schnell in die ewige Schleife fallen, aber sagen wir mal, dass das Problem fix_the_ wahr zurückgibt, dann versuchen wir es erneut. Wie würden Sie dieses Problem lösen, wenn es in Java keine solche Sache gibt? Was wäre Ihr bester Design-Code, um dieses Problem zu lösen?

Dies ist wie eine philosophische Frage, da ich bereits weiß, dass das, was ich verlange, nicht direkt von Java unterstützt wird.

162
Andres Farias

Sie müssen Ihren try-catch wie folgt in eine while-Schleife einschließen: -

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;
    }
}

Ich habe count und maxTries genommen, um zu vermeiden, in eine Endlosschleife zu laufen, falls die Ausnahme in Ihrem try block auftritt.

249
Rohit Jain

Obligatorische "unternehmerische" Lösung:

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);
            }
        }
    }
}

Und anrufen:

OperationHelper.doWithRetry(5, new Operation() {
    @Override public void doIt() {
        //do some stuff
    }
    @Override public void handleException(Exception cause) {
        //recover from the Exception
    }
});
46
ach

Wie immer hängt das beste Design von den jeweiligen Umständen ab. Normalerweise schreibe ich so etwas wie:

for (int retries = 0;; retries++) {
    try {
        return doSomething();
    } catch (SomeException e) {
        if (retries < 6) {
            continue;
        } else {
            throw e;
        }
    }
}
31
meriton

Obwohl try/catch in while eine bekannte und gute Strategie ist, möchte ich Ihnen einen rekursiven Aufruf vorschlagen:

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);
    }
}
19
AlexR

Sie können AOP- und Java-Annotationen aus jcabi-aspect verwenden (ich bin Entwickler):

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

Sie können auch @Loggable- und @LogException-Anmerkungen verwenden.

13
yegor256

Ihr genaues Szenario wird mit Failsafe behandelt:

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

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

Ziemlich einfach.

11
Jonathan

Die meisten dieser Antworten sind im Wesentlichen gleich. Meins ist auch, aber dies ist die Form, die ich mag

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);
}
6
Stephen P

Verwenden Sie eine while-Schleife mit dem lokalen status-Flag. Initialisieren Sie das Flag als false und setzen Sie es auf true, wenn die Operation erfolgreich ist, z. unten:

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

Dies wird solange wiederholt, bis es erfolgreich ist.

Wenn Sie nur eine bestimmte Anzahl von Versuchen wiederholen möchten, verwenden Sie auch einen Zähler:

  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
  }

Dies wird maximal 10 Mal versuchen, falls dies nicht erfolgreich war, und es wird beendet, wenn es zuvor erfolgreich war.

3
Yogendra Singh

Eine einfache Möglichkeit, das Problem zu lösen, besteht darin, Try/Catch in eine while-Schleife einzuwickeln und eine Zählung beizubehalten. Auf diese Weise können Sie eine Endlosschleife verhindern, indem Sie den Zählerstand mit einer anderen Variablen vergleichen und dabei ein Protokoll Ihrer Fehler aufrechterhalten. Es ist nicht die exquisiteste Lösung, aber es würde funktionieren.

2
Jordan Kaye

Für den Fall, dass es nützlich ist, ein paar weitere Optionen in Betracht zu ziehen, die alle zusammen geworfen werden (stopfile anstelle von Wiederholungen, Schlaf, weitere Schleife fortsetzen), möglicherweise alle hilfreich.

 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
}
1
rogerdpack

Sie können https://github.com/bnsd55/RetryCatch verwenden.

Beispiel:

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());

Anstelle von new ExampleRunnable() können Sie Ihre eigene anonyme Funktion übergeben.

1
bnsd55

folgendes ist meine Lösung mit sehr einfacher Vorgehensweise!

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

                    }
                }
0
David Kayo

Hier ein wiederverwendbarer und allgemeiner Ansatz für Java 8+, für den keine externen Bibliotheken erforderlich sind:

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;
            }
        }
    }
}

Verwendungszweck:

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

Ich bin mir nicht sicher, ob dies der "professionelle" Weg ist, und ich bin nicht ganz sicher, ob es für alles funktioniert.

boolean gotError = false;

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

Verwenden Sie einen Do-while-Block, um den Wiederholungsblock zu entwerfen.

boolean successful = false;
int maxTries = 3;
do{
  try {
    something();
    success = true;
  } catch(Me ifUCan) {
    maxTries--;
  }
} while (!successful || maxTries > 0)
0
Rahul Malhotra

Dies ist eine alte Frage, aber eine Lösung ist immer noch relevant. Hier ist meine generische Lösung in Java 8 ohne Verwendung einer Drittanbieter-Bibliothek:

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;
    }
}

Lassen Sie uns einen Testfall haben wie:

@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();
    }
}
0
GirishB

Das Problem bei den verbleibenden Lösungen ist, dass die korrespondierende Funktion kontinuierlich ohne ein Zeitintervall dazwischen versucht und somit den Stapel überläuft.

Warum nicht nur trying nur jede Sekunde und ad eternum ?

Hier eine Lösung mit setTimeout und einer rekursiven Funktion: 

(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);

Ersetzen Sie die Funktion Run() durch die Funktion oder den Code, den Sie jede Sekunde erneut try erneut aufrufen möchten.

Alles, was ein Try-Catch tut, ist, dass Ihr Programm ordnungsgemäß ausfällt. In einer catch -Anweisung versuchen Sie im Allgemeinen, den Fehler zu protokollieren, und machen Sie ggf. Änderungen rückgängig. 

bool finished = false;

while(finished == false)
{
    try
    {
        //your code here
        finished = true
    }
    catch(exception ex)
    {
        log.error("there was an error, ex");
    }
}
0
Sam I am

Spring AOP und Annotation basierte Lösung:

Verwendung (@RetryOperation ist unsere benutzerdefinierte Anmerkung für den Job):

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

Dazu benötigen wir zwei Dinge: 1. eine Anmerkungsschnittstelle und 2. einen Frühjahrsaspekt. Hier ist eine Möglichkeit, diese zu implementieren:

Die Anmerkungsoberfläche:

import Java.lang.annotation.*;

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

Der Frühlingsaspekt:

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;
    }
}
0
Vivek Sethi

https://github.com/tusharmndr/retry-function-wrapper/tree/master/src/main/Java/io

int MAX_RETRY = 3; 
RetryUtil.<Boolean>retry(MAX_RETRY,() -> {
    //Function to retry
    return true;
});
0
tushar Mandar

Ich weiß, dass es hier schon viele ähnliche Antworten gibt, und meine ist nicht viel anders, aber ich werde es trotzdem posten, da es sich um einen bestimmten Fall handelt.

Wenn Sie mit facebook Graph API in PHP umgehen, wird manchmal ein Fehler angezeigt. Wenn Sie dasselbe erneut versuchen, wird dies jedoch zu einem positiven Ergebnis führen (aus verschiedenen magischen Internet-Gründen, die außerhalb der Frage liegen). In diesem Fall ist es nicht nötig, zu beheben einen Fehler, sondern es einfach erneut zu versuchen, da eine Art "Facebook-Fehler" aufgetreten ist.

Dieser Code wird unmittelbar nach dem Erstellen einer Facebook-Sitzung verwendet:

//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(); }
    }
}

Da die Schleife for auf null heruntergefahren wird ($attempt--), ist es relativ einfach, die Anzahl der Versuche in der Zukunft zu ändern.

0
KnightHawk