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


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

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

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

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

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

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

18 143

18 ответов:

нужно заключить в 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.

обязательное решение "предприимчивости":

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

как обычно, лучший дизайн зависит от конкретных обстоятельств. Обычно, хотя, я пишу что-то вроде:

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

хотя 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);
    }
}

вы можете использовать AOP и Java аннотации от jcabi-аспекты (Я разработчик):

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

вы также можете использовать @Loggable и @LogException Примечание.

ваш точный сценарий обрабатывается через Failsafe:

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

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

довольно простой.

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

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

использовать 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 раз, если не удастся до тех пор, пока не выйдет, если его успешно перед рукой.

простой способ решить эту проблему - обернуть try / catch в цикл while и поддерживать счетчик. Таким образом, вы можете предотвратить бесконечный цикл, проверяя счетчик по какой-либо другой переменной, сохраняя при этом журнал Ваших сбоев. Это не самое изысканное решение, но оно будет работать.

в случае, если это полезно, еще несколько вариантов для рассмотрения, все вместе взятые (stopfile вместо повторов, сна, продолжения большего цикла) все возможно полезно.

 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
}

вы можете использовать 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() вы можете передать свою собственную анонимную функцию.

все, что делает 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");
    }
}

использовать делать-а проектировать заново попробовать заблокировать.

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

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

при общении с facebook Graph API in 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--) это делает его довольно легко изменить количество попыток в будущем.

следующее мое решение с очень простым подходом!

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

                    }
                }

Я не уверен, что это "профессиональный" способ сделать это, и я не совсем уверен, что это работает для всего.

boolean gotError = false;

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

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

здесь многоразовый и более общий подход для 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();
    });
}