Как вы реализуете повторную попытку?


203

Try-catch призван помочь в обработке исключений. Это как-то означает, что это поможет нашей системе быть более устойчивой: попытаться восстановиться после неожиданного события.

Мы подозреваем, что что-то может произойти при выполнении команды и инструкции (отправке сообщения), поэтому оно включается в попытку. Если случается что-то почти неожиданное, мы можем что-то сделать: мы пишем улов. Я не думаю, что мы позвонили, чтобы просто зарегистрировать исключение. Мне кажется, что блок catch предназначен для того, чтобы дать нам возможность восстановления после ошибки.

Теперь, допустим, мы оправились от ошибки, потому что мы могли исправить то, что было не так. Это может быть супер приятно повторить попытку:

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

Это быстро попадет в вечный цикл, но допустим, что fix_the_problem возвращает true, а затем мы повторяем попытку. Учитывая, что в Java такого нет, как бы ВЫ решили эту проблему? Какой ваш лучший дизайнерский код для решения этой проблемы?

Это похоже на философский вопрос, учитывая, что я уже знаю, что то, что я спрашиваю, напрямую не поддерживается Java.


5
Что это за исключение?
Беш Гурунг

23
Мне нравится название вашего исключения, хотя. ;)
Рохит Джайн

На самом деле, есть не так много исключений, из которых вы можете восстановить. Я признаю, что моя первоначальная мотивация была не настоящим исключением, а способом избежать, если это произойдет почти никогда: я стараюсь remove()из того java.util.Queue, что теряет и InvalidElementExceptionкогда очередь пуста. Вместо того, чтобы спрашивать, пусто ли оно, я перебираю действия в try-catch (что при параллельности становится обязательным даже при предыдущем if). В таком случае в catchблоке я бы попросил пополнить очередь большим количеством элементов, а затем повторить попытку. Вуаля.
Андрес Фариас

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

Ответы:


305

Вы должны заключить свой цикл 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противном случае он столкнется с тем, что пользователь постоянно вводит неверные данные, и, следовательно, не будет выходить. Пожалуйста, хотя. :)
Рохит Джайн

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

2
Можно ли добавить функцию Thread.sleep () в зацепку здесь? Потому что в некоторых случаях, как ожидание ответа страницы в библиотеке Selenium, это стало критическим. Спасибо.
Суат Атан PhD

2
Прекрасно работает! Для начинающих: если вы получаете положительный бесконечный цикл, проверьте, не добавили ли вы «break;» в конце в блоке try.
Кшиштоф Вальчевски

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: for (int retries = 0; retries <6; retries ++) ??
Дидье А.

8
Потому что я хочу добавить только последнюю попытку, и поэтому блок catch нуждается в этом условии, делая условие в for избыточным.
Меритон

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аннотации.


Вот Это Да ! Звучит необычно! :)
Alind Billore

Должен быть лучший ответ.
Мохамед Тахер Alrefaie

2
Есть ли способ «исправить» ошибку, если попытка не удалась (есть ли усыновления, которые могут исправить следующую попытку)? см вопрос: fix_the_problem();в блоке поймать
warch

Учитывая количество открытых проблем и время, потраченное на исправление подтвержденных ошибок, я бы не стал полагаться на эту библиотеку.
Майкл Лихс

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()- но это формат общего подхода; детали будут зависеть от конкретного случая. Есть также ретрилер на основе гуавы, на который я смотрел.
Стивен П

4

Spring AOP и решение на основе аннотаций:

Использование ( @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время, вы можете просто выйти, когда успех верен.
Рохит Джайн

1
@RohitJain: это выглядит более чистым для меня.
Йогендра Сингх

@YogendraSingh .. Странно. так как вы не изменяете свое место successв вашем catch. Таким образом, это кажется излишним, чтобы проверить это, при каждом запуске catch.
Рохит Джайн

@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 в цикл while и сохранить счетчик. Таким образом, вы можете предотвратить бесконечный цикл, сверяя счет с какой-либо другой переменной, сохраняя журнал ваших сбоев. Это не самое изысканное решение, но оно бы сработало.


1

Используйте do-while для разработки повторной попытки блока.

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

2
Код должен
выдать

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

сон там не очевиден, так как цикл while не будет ждать
João Pimentel Ferreira

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

Все, что делает Try-Catch, это позволяет вашей программе корректно завершиться с ошибкой. В операторе catch вы обычно пытаетесь зарегистрировать ошибку и, если нужно, откатить изменения.

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 Потому что это только один другой персонаж. Все они сводятся к одному и тому же. В C # я использую метод расширения String, IsPopulated()который просто возвращает, !IsNullOrEmpty()чтобы убедиться, что мои намерения понятны всем разработчикам.
Майкл Блэкберн

0

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

При работе с facebook Graph APIin PHPиногда вы получаете сообщение об ошибке, но немедленное повторение одной и той же вещи даст положительный результат (по различным магическим причинам Интернета, которые выходят за рамки этого вопроса). В этом случае нет необходимости исправлять какую-либо ошибку, нужно просто повторить попытку, потому что произошла какая-то «ошибка на Facebook».

Этот код используется сразу после создания сессии в 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

Попробуйте использовать аннотацию springs @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.