Jak wdrożyć próbę ponownego uruchomienia?


203

Try-catch ma na celu pomóc w obsłudze wyjątków. To w jakiś sposób pomoże naszemu systemowi stać się bardziej niezawodnym: spróbuj odzyskać dane z nieoczekiwanego zdarzenia.

Podejrzewamy, że coś może się wydarzyć podczas wykonywania instrukcji i (wysyłania wiadomości), więc zostaje to uwzględnione podczas próby. Jeśli wydarzy się coś prawie nieoczekiwanego, możemy coś zrobić: zapisujemy haczyk. Nie sądzę, że zadzwoniliśmy, aby po prostu zarejestrować wyjątek. Wydaje mi się, że blok catch ma dać nam szansę na wyleczenie z błędu.

Teraz załóżmy, że usuwamy błąd, ponieważ możemy naprawić to, co jest nie tak. Ponownie spróbuj ponownie:

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

Szybko wpadłoby to w wieczną pętlę, ale powiedzmy, że fix_the_problem zwraca true, a następnie ponawiamy próbę. Biorąc pod uwagę, że nie ma czegoś takiego w Javie, w jaki sposób rozwiążesz ten problem? Jaki byłby twój najlepszy kod do rozwiązania tego problemu?

To jest pytanie filozoficzne, biorąc pod uwagę, że już wiem, o co pytam, nie jest bezpośrednio wspierane przez Javę.


5
Co to za wyjątek?
Bhesh Gurung

23
Podoba mi się nazwa twojego wyjątku. ;)
Rohit Jain

W rzeczywistości nie ma wielu wyjątków, z których można się zregenerować. Przyznaję, że moja początkowa motywacja nie była prawdziwym wyjątkiem, ale sposobem na uniknięcie tego, co się stanie, prawie nigdy: staram się remove()od tego java.util.Queue, który przeskakuje i InvalidElementExceptionkiedy kolejka jest pusta. Zamiast pytać, czy jest pusty, zbieram akcje w try-catch (co w przypadku współbieżności staje się obowiązkowe nawet w przypadku poprzedniego, jeśli). W takim przypadku w catchbloku poprosiłbym o uzupełnienie kolejki o więcej elementów, a następnie ponowienie próby. Voila
Andres Farias,

1
Widzę, że zwykłym sposobem jest dostęp do bazy danych, jeśli połączenie nie powiedzie się, ponowne połączenie, jeśli się nie powiedzie, wrzuć poważny wyjątek, w przeciwnym razie ponów połączenie. Jak już powiedziano, moglibyśmy to zrobić w pętli ze sprawdzeniem u dołu, jeśli (błąd <> 0), a następnie wrócić w przeciwnym razie przerwać;
Theresa Forster

Odpowiedzi:


304

Musisz zamknąć swoją try-catchwewnętrzną whilepętlę w następujący sposób: -

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

Wziąłem counti maxTriesunikam wpadania w nieskończoną pętlę, na wypadek, gdyby wyjątek nadal występował w twoim try block.


3
Na początku myślałem w czymś takim, bez maxTries. Dziękuję za odpowiedź!
Andres Farias

6
@AndresFarias .. Tak, najważniejszym punktem w tej odpowiedzi jest uwzględnienie maxTries. W przeciwnym razie zostanie on napotkany, infinite loopjeśli użytkownik będzie ciągle podawał nieprawidłowe dane wejściowe, a zatem nie wyjdzie. Nie ma za co. :)
Rohit Jain

dziękuję za to - uratowało mnie to od konieczności napisania jakiegoś bardzo prostego kodu!
David Holiday

2
Czy jest możliwe dodanie tutaj funkcji Thread.sleep () wewnątrz haczyka? Ponieważ w niektórych przypadkach na przykład oczekiwanie na odpowiedź strony w bibliotece Selenium stało się krytyczne. Dzięki.
Suat Atan PhD

2
Działa świetnie! Dla początkujących: Jeśli pojawi się dodatnia nieskończona pętla, sprawdź, czy dodałeś „break;” na końcu w bloku „try”.
Krzysztof Walczewski,

59

Obowiązkowe rozwiązanie „przedsiębiorczości”:

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

I zadzwonić:

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

6
Powinieneś rzucić wyjątek, jeśli ostatnia próba się nie powiedzie, tak jak w innych podanych odpowiedziach.
cvacca

35

Jak zwykle najlepszy projekt zależy od konkretnych okoliczności. Zazwyczaj jednak piszę coś takiego:

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

Poczekaj, dlaczego nie ma warunku w deklaracji pętli for, takiego jak: for (int ponów = 0; ponów <6; ponów ++)?
Didier A.

8
Ponieważ chcę rzucić tylko w ostatniej próbie, a zatem blok przechwytywania potrzebuje tego warunku, co powoduje, że warunek jest zbędny.
meriton

1
Nie wydaje mi się, że continuejest to potrzebne. Możesz po prostu odwrócić warunek if.
Koray Tugay,

19

Chociaż try/catchw whileto dobrze znana i dobra strategia, chcę zaproponować Ci rekurencyjne połączenie:

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

41
W jaki sposób rekurencja jest lepsza niż pętla dla tego przypadku użycia?
Dan.

7
Śledzenie stosu może wyglądać nieco dziwnie na tym, ponieważ czy nie limitliczyłoby to na powtórzenie metody? W przeciwieństwie do wersji pętli, która będzie rzucać na poziomie „oryginalnym” ...
Clockwork-Muse

7
Oczywiście na papierze wygląda elegancko, ale nie jestem pewien, czy rekursja jest jakoś właściwym podejściem.
Thomas

3
Nie rozumiem też, dlaczego tutaj jest rekurencja. W każdym razie myślę, że można to uprościć:void retry(int times) { (...) if (times==0) throw w; retry(times--);
sinuhepop,

8
Złą praktyką jest stosowanie rekurencji jako substytutu samej iteracji. Rekurencja jest używana, gdy chcesz przesłać i pop niektóre dane.
Markiz Lorne

19

Dokładny scenariusz obsługiwany przez Failsafe :

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

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

Dość proste.


5
bardzo ładna biblioteka.
Maksim

dla tych, którzy zastanawiają się, będziesz potrzebować tego w zależnościach stopni - skompiluj 'net.jodah: Failafeafe: 1.1.0'
Shreyas

18

Możesz używać adnotacji AOP i Java z aspektów jcabi (jestem programistą):

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

Możesz także użyć @Loggablei @LogExceptionadnotacji.


Łał ! Brzmi fantastycznie! :)
Alind Billore,

Powinna być najlepsza odpowiedź.
Mohamed Taher Alrefaie

2
czy istnieje sposób na „naprawienie” błędu, gdy próba się nie powiedzie (czy są jakieś adopcje, które mogą naprawić kolejną próbę)? patrz pytanie: fix_the_problem();w bloku catch
warch

Biorąc pod uwagę liczbę otwartych problemów i czas, jaki upłynął na naprawienie potwierdzonych błędów, nie polegałbym na tej bibliotece.
Michael Lihs,

6

Większość tych odpowiedzi jest zasadniczo taka sama. Mój też jest, ale to jest forma, którą lubię

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

Wadą jest to, że dzwonisz również fix_the_problempo ostatniej próbie. To może być kosztowna operacja i może zmarnować trochę czasu.
Joachim Sauer

2
@JoachimSauer True. Mógłbyś if (tryCount < max) fix()- ale jest to format ogólnego podejścia; szczegóły zależą od konkretnego przypadku. Jest też Retryer na bazie guawy, na który patrzyłem.
Stephen P

4

Wiosenne AOP i rozwiązanie oparte na adnotacjach:

Użycie ( @RetryOperationto nasza niestandardowa adnotacja do zadania):

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

Będziemy potrzebować dwóch rzeczy, aby to osiągnąć: 1. interfejs adnotacji i 2. aspekt wiosenny. Oto jeden ze sposobów ich realizacji:

Interfejs adnotacji:

import java.lang.annotation.*;

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

Aspekt wiosenny:

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

3

Użyj whilepętli z statusflagą lokalną . Zainicjuj flagę jako falsei ustaw ją na, truegdy operacja się powiedzie, np. Poniżej:

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

Spowoduje to ponawianie próby aż do pomyślnego zakończenia.

Jeśli chcesz spróbować ponownie tylko określoną liczbę razy, użyj również licznika:

  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
  }

Spróbuje to maksymalnie 10 razy, jeśli nie powiedzie się, do tego czasu wyjdzie, jeśli zakończy się sukcesem przed rozdaniem.


Zamiast sprawdzać !successw swoim czasie, możesz po prostu wyrwać się, gdy sukces jest prawdziwy.
Rohit Jain

1
@RohitJain: Dla mnie wygląda to bardziej czysto.
Yogendra Singh,

@YogendraSingh .. Dziwne. ponieważ nie modyfikujesz swojego successnigdzie w swoim catch. Więc wydaje się zbędne sprawdzanie tego przy każdym uruchomieniu catch.
Rohit Jain

@RohitJain: Catch tylko koryguje dane. Wróci i ponownie uruchomi instrukcję. Jeśli się powiedzie, zmodyfikuje success. Wypróbuj to.
Yogendra Singh,

3

To stare pytanie, ale rozwiązanie jest nadal aktualne. Oto moje ogólne rozwiązanie w Javie 8 bez użycia biblioteki innej firmy:

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

Przygotujmy przypadek testowy, taki jak:

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

fajny pomysł, budowniczy od tego!
HankTheTank

2

Prostym sposobem rozwiązania tego problemu byłoby zawinięcie pętli try / catch w pętlę while i utrzymanie zliczania. W ten sposób można zapobiec nieskończonej pętli, sprawdzając liczbę innych zmiennych, zachowując dziennik błędów. To nie jest najbardziej wykwintne rozwiązanie, ale zadziałałoby.


1

Użyj do-while, aby zaprojektować blok ponownej próby.

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

2
Kod powinien wyrzucić oryginalny wyjątek, jeśli się nie powiedzie
lilalinux

1

Jeśli jest to użyteczne, należy rozważyć kilka innych opcji, wszystkie razem zebrane (plik stop zamiast prób, sen i kontynuuj większą pętlę) - wszystko to może być pomocne.

 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
}

Drodzy wyborcy, proszę zostawcie komentarze, dlaczego, dzięki!
rogerdpack

1
Jest to zwykła ignorancja, aby głosować i nie podawać powodu.
xploreraj

spanie tam nie jest oczywiste, ponieważ pętla while nie czekałaby
João Pimentel Ferreira

1

Możesz użyć https://github.com/bnsd55/RetryCatch

Przykład:

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

Zamiast tego new ExampleRunnable()możesz przekazać własną anonimową funkcję.


1

Jeśli nie wszystkie wyjątki wymagają ponownej próby, tylko niektóre. A jeśli trzeba wykonać przynajmniej jedną próbę, oto alternatywna metoda użyteczności:

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

Stosowanie:

    runWithRetry(() -> {
       // do something
    }, TimeoutException.class, 5)

0

Wszystko, co robi Try-Catch, pozwala na awarię twojego programu. W instrukcji catch na ogół próbujesz zapisać błąd, a jeśli to konieczne, możesz wycofać zmiany.

bool finished = false;

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

masz na myśli w przeciwieństwie do (!finished)?
Sam, jestem, mówi Przywróć Monikę

1
@RohitJain wygląda za bardzo while(finished). Wolę używać bardziej pełnej wersji.
Sam, jestem, mówi Przywróć Monikę

3
Jak do cholery while(!finished)wygląda while (finished)?
Rohit Jain

@Rohit Ponieważ to tylko jedna postać inna. Wszystkie zostały skompilowane do tego samego. W języku C # używam metody rozszerzenia String, IsPopulated()która właśnie powraca, !IsNullOrEmpty()aby upewnić się, że moje zamiary są zrozumiałe dla wszystkich programistów.
Michael Blackburn,

0

Wiem, że jest już wiele podobnych odpowiedzi i moja niewiele się różni, ale i tak opublikuję, ponieważ dotyczy konkretnej sprawy / problemu.

Gdy mamy do czynienia z facebook Graph APIna PHPwas czasami pojawia się błąd, ale natychmiast ponownie próbuje to samo da wynik pozytywny (dla różnych Magical powodów internetowych, które są poza zakresem tej kwestii). W takim przypadku nie ma potrzeby naprawy żadnego błędu, ale po prostu spróbować ponownie, ponieważ wystąpił pewien rodzaj „błędu na Facebooku”.

Ten kod jest używany natychmiast po utworzeniu sesji na Facebooku:

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

Ponadto forodliczanie pętli do zera ( $attempt--) ułatwia zmianę liczby prób w przyszłości.


0

Oto moje rozwiązanie z bardzo prostym podejściem!

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

                    }
                }

1
spójrz na odpowiedź @Rohit Jain, która jest bardziej szczegółowa i nie jest nieskończoną pętlą w przypadkach negatywnych.
Chandra Shekhar

0

Nie jestem pewien, czy jest to „profesjonalny” sposób na zrobienie tego i nie jestem całkowicie pewien, czy to działa na wszystko.

boolean gotError = false;

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


0

Oto wielokrotnego użytku i bardziej ogólne podejście do Java 8+, które nie wymaga bibliotek zewnętrznych:

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

Stosowanie:

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

0

Problem z pozostałymi rozwiązaniami polega na tym, że funkcja korespondenta próbuje nieprzerwanie bez przerwy między nimi, co powoduje nadmierne zalanie stosu.

Dlaczego nie tylko przychodzićtry co sekundę i po wieczność ?

Oto rozwiązanie wykorzystujące setTimeouti funkcja rekurencyjna:

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

Zastąp funkcję Run()funkcją lub kodem, który chcesz powtarzać tryco sekundę.


0

Spróbuj użyć sprężyn @Retable Adnotation, poniższa metoda spróbuje ponownie wykonać 3 próby, gdy wystąpi wyjątek RuntimeException

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

0

Poniżej fragmentu kodu należy wykonać fragment kodu. Jeśli wystąpił błąd podczas wykonywania fragmentu kodu, uśpij przez M milisekund i spróbuj ponownie. Link referencyjny .

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

0

Oto moje rozwiązanie podobne do niektórych innych, które może zawijać funkcję, ale pozwala ci uzyskać wartość zwracaną przez funkcje, jeśli się ona wywoła.

    /**
     * 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");
    }
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.