首页 文章

你如何实现重新尝试?

提问于
浏览
156

Try-catch旨在帮助进行异常处理 . 这意味着它将以某种方式帮助我们的系统更加健壮:尝试从意外事件中恢复 .

我们怀疑在执行和指令(发送消息)时可能会发生某些事情,因此它会被包含在try中 . 如果发生几乎意外的事情,我们可以做点什么:我们写下了捕获 . 我认为我们没有打电话来记录异常 . 我认为catch块意味着让我们有机会从错误中恢复 .

现在,假设我们可以从错误中恢复,因为我们可以修复错误 . 重做是非常好的:

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

这将很快落入永恒循环,但是假设fix_the_problem返回true,那么我们重试 . 鉴于Java中没有这样的东西,你将如何解决这个问题?解决这个问题的最佳设计代码是什么?

这就像一个哲学问题,因为我已经知道我所要求的并不是Java直接支持的 .

20 回答

  • 45

    虽然 try/catchwhile 是众所周知的好策略我想建议你递归调用:

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

    您可以使用jcabi-aspects的AOP和Java注释(我是开发人员):

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

    您还可以使用 @Loggable@LogException 注释 .

  • 3

    您的确切方案通过Failsafe处理:

    RetryPolicy retryPolicy = new RetryPolicy()
      .retryOn(NearlyUnexpectedException.class);
    
    Failsafe.with(retryPolicy)
      .onRetry((r, f) -> fix_the_problem())
      .run(() -> some_instruction());
    

    很简单 .

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

    你需要将 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;
        }
    }
    

    我已经采取 countmaxTries 以避免遇到无限循环,以防异常继续发生在 try block 中 .

  • 0

    强制性的“企业家”解决方案:

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

    像往常一样,最好的设计取决于具体情况 . 通常,我写的东西如下:

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

    使用带有本地 status 标志的 while 循环 . 将标志初始化为 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次,直到那时,如果事先成功,将退出 .

  • 0

    解决问题的一种简单方法是将try / catch包装在while循环中并保持计数 . 这样,您可以通过在保持故障日志的同时检查其他变量的计数来防止无限循环 . 它不是最精致的解决方案,但它会起作用 .

  • 238

    如果它有用,还需要考虑几个选项,所有这些选项一起抛出(stopfile而不是重试,sleep,继续更大的循环)都可能有用 .

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

    所有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");
        }
    }
    
  • 0

    使用do-while设计重试块 .

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

    我知道这里已经有很多相似的答案,而且我的情况并没有太大的不同,但无论如何我会发布它,因为它涉及一个特定的案例/问题 .

    当你在 PHP 中处理 facebook Graph API 时,你有时会得到一个错误,但是立即重新尝试同样的事情会给出一个积极的结果(因为各种神奇的互联网原因超出了这个问题的范围) . 在这种情况下,没有必要修复任何错误,但只是再次尝试,因为有某种"facebook error" .

    创建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) {
    
                        }
                    }
    
  • 19

    我不确定这是否是“专业”方式,我不完全确定它是否适用于所有事情 .

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

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

    剩下的解决方案的问题在于,对应的函数没有时间连续尝试介于两者之间的间隔,因此溢出了堆栈 .

    为什么不仅仅是每一秒钟和每一次?

    这里使用 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);
    

    用你想要每秒重复 try 的函数或代码替换函数 Run() .

  • 2

    Spring AOP和基于注释的解决方案:

    用法( @RetryOperation 是我们的工作自定义注释):

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

    我们需要做两件事:1 . 注释界面,2 . spring 方面 . 以下是实现这些的一种方法:

    注释界面:

    import java.lang.annotation.*;
    
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface RetryOperation {
        int retryCount();
        int waitSeconds();
    }
    

    Spring 天看点:

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

相关问题