Try-catch是用来帮助异常处理的。这意味着它将在某种程度上帮助我们的系统变得更加健壮:尝试从意外事件中恢复。

我们怀疑在执行和指令(发送消息)时可能会发生一些事情,因此它被包含在try中。如果发生了几乎意想不到的事情,我们可以做些什么:我们编写捕获。我不认为我们调用只是为了记录异常。我认为catch块是为了给我们从错误中恢复的机会。

现在,假设我们从错误中恢复,因为我们可以修复错误。如果能再试一次,那就太好了:

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

这将很快陷入永恒循环,但让我们假设fix_the_problem返回true,然后重试。假设在Java中没有这样的东西,你将如何解决这个问题?你认为解决这个问题的最佳设计代码是什么?

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


当前回答

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

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

其他回答

这些答案基本上都是一样的。我的也是,但我喜欢这种形式

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

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

此解决方案允许您配置一个可重用的功能,以便在不使用任何外部库的情况下基于特定异常进行重试

//创建一个适合你需要的函数。

@FunctionalInterface
public interface ThrowableBiFunction<U,T,R> {
    R apply(U u ,T t) throws Exception;
}

//这是解决方案的关键

public interface ExceptionRetryable<T, U, R> {
    
 int getRetries();
   
 List<Class<? extends Exception>> getRetryableExceptions();

    default R execute(ThrowableBiFunction<T, U, R> function, T t, U u) throws Exception {
        int numberOfRetries = getRetries();
        return execute(function, t, u, numberOfRetries);
    }

    default R execute(ThrowableBiFunction<T, U, R> function, T t, U u, int retryCount) throws Exception {
        try {
            log.info(" Attempting to execute ExceptionRetryable#execute ,Number of remaining retries {} ",retryCount);
            return function.apply(t, u);
        } catch (Exception e) {

           log.info(" error occurred in ExceptionRetryable#execute",e);
            if (retryCount == 0)
                throw e;
            for (Class exp : getRetryableExceptions()) {
                if (e.getClass() == exp) {
                   return execute(function, t, u, retryCount - 1);
                }
            }
            throw e;
        }

    }
}

//创建一个异常可重执行的实现

public class TestRetryable implements ExceptionRetryable<String, String, List<String>> {
    @Override
    public int getRetries() {
        return 10;
    }

    @Override
    public List<Class<? extends Exception>> getRetryableExceptions() {
        return Arrays.asList(new Exception1().getClass(), new Exception2().getClass());
        ;
    }
}

//最后创建一个throwablebiffunction,它封装了需要在exception上重试的代码段和ExceptionRetryable实例

 TestRetryable retryable = new TestRetryable();
    ThrowableBiFunction<Integer,Long, String> testRetrablefcn = { i, l ->
           // your code goes here
};
    Integer i = 0;
    Long l = 1l;
      String output = testRetrablefcn.execute(testRetrablefcn,i,l); 

使用do-while设计重试块。

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

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

用法(@RetryOperation是作业的自定义注释):

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

要做到这一点,我们需要做两件事:1。注释接口;一个春天的方面。下面是实现这些的一种方法:

标注接口:

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