Як ви реалізуєте повторну спробу?


203

Try-catch має допомогти у вирішенні випадків. Це якось означає, що це допоможе нашій системі бути більш надійною: спробуйте відновитися після несподіваної події.

Ми підозрюємо, що може трапитися щось під час виконання та вказівки (надсилання повідомлення), тому воно вкладається в спробу. Якщо трапиться щось майже несподіване, ми можемо щось зробити: пишемо улов. Я не думаю, що ми закликали просто записати виняток. Я вважаю, що блок ловлі має на меті дати нам можливість відновитись після помилки.

Скажімо, ми відновилися після помилки, оскільки ми могли виправити те, що було не так. Було б дуже приємно зробити повторну спробу:

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

Це швидко потрапить у вічний цикл, але скажімо, що fix_the_problem повертає справжнє значення, тоді ми повторимо спробу. Зважаючи на те, що на Java немає такого, як би ви вирішили цю проблему? Який найкращий дизайн-код для вирішення цього питання?

Це як філософське запитання, враховуючи те, що я вже знаю, про що я прошу, не підтримує Java безпосередньо.


5
Який виняток це?
Bhesh Gurung

23
Мені подобається ім’я вашого виключення. ;)
Rohit Jain

У вчинку не так багато винятків, від яких можна оговтатися. Я визнаю, що моя початкова мотивація не була справжнім винятком, але спосіб уникнути випадків, коли це станеться майже ніколи: я намагаюся зробити remove()з java.util.Queue, який шипить і InvalidElementExceptionколи черга порожня. Замість того, щоб запитувати, чи це порожньо, я розбираю дії в спробі лову (що за умови одночасності стає обов'язковим навіть для попереднього, якщо). У такому випадку в catchблоці я б попросив поповнити чергу ще кількома елементами, а потім повторити спробу. Вуаля.
Андрес Фаріас

1
Я можу побачити, що звичайний спосіб зробити це було б для доступу до БД, якщо з'єднання не вдалося знову підключитися, якщо воно не вдалося, то киньте основний виняток, інакше повторіть виклик ще раз. Як вже було сказано, ми могли б зробити це в циклі з чеком внизу, якщо (помилка <> 0), тоді повернемося в іншому випадку, перервіться;
Тереза ​​Форстер

Відповіді:


304

Вам потрібно укласти try-catchвнутрішню whileпетлю так: -

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

Я взяв countі maxTriesщоб уникнути нескінченного циклу, якщо виняток продовжується у вашому try block.


3
Я подумав щось подібне спочатку, без maxTries. Дякую за відповідь!
Андрес Фаріас

6
@AndresFarias .. Так, найважливішим моментом у цій відповіді є включення а maxTries. В іншому випадку він зіткнеться з тим, infinite loopякщо користувач невпинно дає неправильний ввід, а значить, не вийде. Ти бажаєш, хоча. :)
Rohit Jain

спасибі за це - це просто врятувало мене від необхідності писати якийсь дуже гнучкий код!
Девід Холідей

2
Чи можливо тут додати функцію Thread.sleep () всередині улову. Тому що в деяких випадках, як очікування відповіді на сторінку в бібліотеці Selenium, що стало критичним. Дякую.
Суат Атан кандидат наук

2
Чудово працює! Для початківців: Якщо у вас є позитивний нескінченний цикл, перевірте, чи додано "перерва"; наприкінці в блоці "спробувати".
Кшиштоф Валчевський

59

Обов’язкове "підприємливе" рішення:

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

І зателефонувати:

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

6
Ви повинні повторно кинути виняток, якщо останній повторний спробу не вдався, як це зроблено в інших наведених відповідях.
cvacca

35

Як завжди, найкращий дизайн залежить від конкретних обставин. Зазвичай, я пишу щось подібне:

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

Зачекайте, чому б не встановити умови всередині оголошення для циклу на зразок: for (int retries = 0; retries <6; retries ++) ??
Дідьє А.

8
Тому що я хочу лише кинути в останній спробі, і тому блок вилову потребує цієї умови, перетворюючи умову на зайве.
meriton

1
Я не думаю, що continueце там потрібно .. І ви можете просто перевернути умову if.
Корай Тугай

19

Хоча try/catchв whileдобре відомий і оптимальної стратегії я хочу запропонувати вам рекурсивний виклик:

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
Чим рекурсія краща за цикл для цього випадку використання?
День

7
Слід стека може виглядати трохи дивним на цьому, бо чи не було б він limitрахувати метод повторення? На відміну від версії циклу, яка буде кинутий на "оригінальний" рівень ...
Clockwork-Muse

7
Звичайно, на папері виглядає елегантно, але я не впевнений, що рекурсія - це правильний підхід.
Томас

3
Я не розумію, чому тут і рекурсії. У будь-якому випадку, я думаю, це могло б бути спрощено до:void retry(int times) { (...) if (times==0) throw w; retry(times--);
sinuhepop

8
Використовувати рекурсію як заміну простої ітерації є поганою практикою. Рекурсія призначена для використання, коли ви хочете натиснути і передати деякі дані.
Маркіз Лорнський

19

Ваш точний сценарій обробляється через Failsafe :

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

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

Досить просто.


5
дуже приємна бібліотека.
Максим

для тих, хто цікавиться, вам це знадобиться у ваших залежностях від gradle - компілюйте 'net.jodah: failsafe: 1.1.0'
Shreyas

18

Ви можете використовувати анотації AOP та Java з аспектів jcabi (я розробник):

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

Ви також можете використовувати @Loggableі @LogExceptionпримітки.


Оце Так ! Звучить фантазія! :)
Алінд Біллор

Повинно відповісти.
Мохаммед Тахер Альрефає

2
чи є спосіб "виправити" помилку, коли спроба не вдасться (зробити деякі усиновлення, які можуть виправити наступну спробу)? дивіться питання: fix_the_problem();у блоці лову
1717

Зважаючи на кількість відкритих випусків та час, що минув, коли підтверджені помилки не виправлені, я б не покладався на цю бібліотеку.
Майкл Ліс

6

Більшість цих відповідей по суті однакові. Моя також є, але мені подобається така форма

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

Одним недоліком є ​​те, що ви дзвоните і fix_the_problemпісля останньої спроби. Це може бути дорогою операцією і може витратити час.
Йоахім Зауер

2
@JoachimSauer Правда. Ви могли б if (tryCount < max) fix()- але це формат загального підходу; деталі залежать від конкретного випадку. Також я шукав ретриєрів на основі гуави .
Stephen P

4

Весняний АОП та рішення на основі анотацій:

Використання ( @RetryOperationце наша спеціальна анотація до роботи):

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

Для цього нам знадобляться дві речі: 1. інтерфейс анотацій та 2. весняний аспект. Ось один із способів їх здійснення:

Інтерфейс анотацій:

import java.lang.annotation.*;

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

Аспект весни:

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

Використовуйте whileцикл з місцевим statusпрапором. Ініціалізуйте прапор як falseі встановіть його, trueколи робота успішна, наприклад нижче:

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

Це триватиме повторних спроб до його успішного.

Якщо ви хочете повторити лише певну кількість разів, тоді також використовуйте лічильник:

  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
  }

Це спробує максимум 10 разів, якщо не буде успішним, тоді вийде, якщо його буде успішно до початку.


Замість того, щоб перевіряти !successсвій час, ви можете просто вирватися з того часу, коли успіх справдиться.
Rohit Jain

1
@RohitJain: Мені це виглядає більш чисто.
Йогендра Сінгх

@YogendraSingh .. Дивно. так як ви не змінюєте свого successмісця в своєму catch. Тому здається, що перевіряти це на кожному циклі catch.
Rohit Jain

@RohitJain: Catch просто виправляє дані. Він повернеться та запустить заяву ще раз. У разі успіху він змінить success. Спробуй.
Йогендра Сінгх

3

Це давнє питання, але рішення все ще актуальне. Ось моє загальне рішення в Java 8 без використання будь-якої сторонньої бібліотеки:

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

Будемо мати тестовий випадок на кшталт:

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

гарна ідея, будівельник сюди!
HankTheTank

2

Простим способом вирішити проблему було б обернути цикл try / catch у певний час та підтримувати кількість. Таким чином, ви могли запобігти нескінченному циклу, перевіривши підрахунок на якусь іншу змінну, зберігаючи журнал ваших відмов. Це не найвишуканіше рішення, але воно спрацює.


1

Використовуйте час, щоб спроектувати блок повторної спроби.

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

2
Код повинен викидати оригінальний виняток, якщо невдало
lilalinux

1

У випадку, якщо це корисно, ще пара варіантів, які слід розглянути, усі разом (стопфіл замість спроб, спати, продовжуйте більший цикл), можливо, корисні.

 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

1
Це відверте ігнорування, а не приводити причину.
xploreraj

спати там не очевидно, оскільки поки петля не чекатиме
Жоао Піментел Феррейра

1

Ви можете використовувати https://github.com/bnsd55/RetryCatch

Приклад:

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

Замість new ExampleRunnable()вас можна передати власну анонімну функцію.


1

Якщо не всі винятки гарантують повторний повтор, лише деякі. І якщо потрібно зробити хоча б одну спробу, ось альтернативний корисний метод:

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

Використання:

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

0

Все, що потрібно спробувати - це дозволить програмі вийти з ладу. У заяві про вилов ви, як правило, намагаєтесь увімкнути помилку та, можливо, відмовити зміни, якщо вам потрібно.

bool finished = false;

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

ти маєш на увазі на противагу (!finished)?
Сем, я кажу: Відновіть Моніку

1
@RohitJain це дуже схоже while(finished). Я вважаю за краще використовувати більш докладну версію.
Сем, я кажу: Відновіть Моніку

3
Як на землі це while(!finished)виглядає while (finished)??
Rohit Jain

@Rohit Тому що це лише один символ. Усі вони складаються до одного і того ж. У C # я використовую метод розширення String, IsPopulated()який просто повертається, !IsNullOrEmpty()щоб гарантувати, що мої наміри зрозуміли всі розробники.
Майкл Блекберн

0

Я знаю, що тут вже багато подібних відповідей, і моя не дуже відрізняється, але я все-таки опублікую її, оскільки вона стосується конкретного випадку / питання.

Коли маєш справу з facebook Graph APIв PHPвас іноді отримую повідомлення про помилку, але тут же знову намагається те ж саме дасть позитивний результат (за різними магічним причин Інтернету, які виходять за рамки даного питання). У цьому випадку не потрібно виправляти будь-яку помилку, а просто спробувати ще раз, оскільки сталася якась «фейсбук-помилка».

Цей код використовується відразу після створення сеансу у 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(); }
    }
}

Крім того, маючи кількість forциклу до нуля ( $attempt--), це дозволяє досить легко змінити кількість спроб у майбутньому.


0

далі моє рішення з дуже простим підходом!

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

                    }
                }

1
будь ласка, подивіться на @Rohit Jain відповідь, яка більш конкретна, а не нескінченна петля в негативних випадках.
Чандра Шехар

0

Я не впевнений, чи це "професійний" спосіб зробити це, і я не зовсім впевнений, чи працює він на все.

boolean gotError = false;

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


0

Ось багаторазовий і більш загальний підхід для Java 8+, який не вимагає зовнішніх бібліотек:

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

Використання:

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

0

Проблема з іншими рішеннями полягає в тому, що кореспондентська функція намагається постійно без тимчасового інтервалу, перекриваючи стек.

Чому б не просто tryтільки щосекунди та ad eternum ?

Ось рішення з використанням setTimeoutта рекурсивної функції:

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

Замініть функцію Run()на функцію або код, який ви хочете повторно tryкожні секунди.


0

Спробуйте скористатись джерелами @Retryable анотація, нижче наведений метод повторить 3 спроби, коли відбудеться 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

Нижче фрагмент виконайте деякий фрагмент коду. Якщо під час виконання фрагмента коду у вас виникли якісь помилки, спайте протягом M мілісекунд і повторіть спробу. Посилання на посилання .

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

Ось моє рішення, подібне до деяких інших, може перетворити функцію, але дозволяє отримати функції повернути значення, якщо це досягає успіху.

    /**
     * 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");
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.