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.
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.
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
}
});
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;
}
}
}
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);
}
}
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.
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.
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);
}
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.
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.
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
}
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.
folgendes ist meine Lösung mit sehr einfacher Vorgehensweise!
while (true) {
try {
/// Statement what may cause an error;
break;
} catch (Exception e) {
}
}
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();
});
}
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 );
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)
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();
}
}
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 try
ing 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");
}
}
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;
}
}
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;
});
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.