新增操作 失败后重试
在我从事的每个项目中,始终需要某些功能:重试操作。 通常,这是关于通过网络的呼叫,该呼叫可能一次失败,但随后会成功。 它可能涉及许多其他内容,主要包括与另一个系统的通信(无论是否通过网络)。 它的功能,你绝对需要在大多数应用中,特别是如果你想他们是高可用性( 如这里指出的 ,例如)。
每次我必须在项目中引入此功能时,我都会检查我们已导入的标准库,没有这样的东西。 因此,我总是最终将我以前的项目中的同一段代码复制粘贴。 我什至不记得我是什么时候第一次介绍它的,但是从那以后我就在“旅行”。 所以这里是:
/**
* Class that provides retrying functionality. Example:
* <p></p>
* <code>
* Callable<String> callable = new Callable<String>() {..};
* String result = RetryableOperation.create(callable).retry(5, IOException.class);
* </code>
*
* @param <T> the return type of the operation
*/
public class RetryableOperation<T> {
private Callable<T> callable;
private Runnable runnable;
private boolean exponentialBackoff;
private int backoffInterval = 500;
/**
* Create a retryable operation based on a Callable instance. The return
* type of retry(..) is the type parameter of the Callable instance.
*
* @param callable
* @return
* a new instance of RetryableOperation
*/
public static <T> RetryableOperation<T> create(Callable<T> callable) {
return new RetryableOperation<T>().withCallable(callable);
}
/**
* Creates a retryable operation based on a Runnable instance. In this case
* the retry(..) method always returns null.
*
* @param runnable
* @return
* a new instance of RetryableOperation
*/
public static RetryableOperation<?> create(Runnable runnable) {
return new RetryableOperation<Object>().withRunnable(runnable);
}
/**
* Retries the operation. Retrying happens regardless of the exception thrown.
*
* @param retries
* number of retries before the exception is thrown to the caller
* @param exceptions
* the operation will be retried only if the exception that occurs is one of the
* exceptions passed in this array
* @return
* the result of the operation (null if Runnable is used instead of Callable)
* @throws Exception
* the exception that occurred on the last attempt
*/
public T retry(int retries, Class<? extends Exception>... exceptions) throws Exception {
if (callable == null && runnable == null) {
throw new IllegalStateException("Either runnable or callable must be set");
}
Set<Class<? extends Exception>> retryFor = new HashSet<Class<? extends Exception>>();
retryFor.addAll(Arrays.asList(exceptions));
for (int i = 0; i < retries; i++) {
try {
if (exponentialBackoff && i > 0) {
int sleepTime = (int) ((Math.pow(2, i) - 1) / 2) * backoffInterval;
Thread.sleep(sleepTime);
}
if (callable != null) {
return callable.call();
} else if (runnable != null) {
runnable.run();
return null;
}
} catch (Exception e) {
if (retryFor.isEmpty() || retryFor.contains(e.getClass())) {
if (i == retries - 1) {
throw e;
}
} else {
// if the exception is not the expected one, throw it immediately
throw e;
}
}
}
// can't be reached - in case of failure on the last iteration the exception is rethrown
return null;
}
private RetryableOperation<T> withCallable(Callable<T> callable) {
this.callable = callable;
return this;
}
private RetryableOperation<T> withRunnable(Runnable runnable) {
this.runnable = runnable;
return this;
}
public RetryableOperation<T> withExponentialBackoff() {
this.exponentialBackoff = true;
return this;
}
}
1
这很简单,但是效果很好。 您可以重试每个失败,也可以重试特定的异常(您不想重试NullPointerException,但是必须配置适当的超时,然后重试网络故障):
Result result = op.retry(3);
...
Result result = op.retry(3, IOException.class);
我什至曾建议番石榴将其包含在内,然后再看其他类似的提案,但据我所知,番石榴或apache commons中都没有这种功能。 而且我不会创建一个新的github项目,因为那将需要在maven Central中管理一个条目,而对于单个实用程序类来说,这是一项很大的工作。
当然,还有其他解决方法,它们具有更大的API和占用空间- 重试番石榴扩展和最近提取为单独的项目spring-retry 。 它们值得检查,并且具有要导入的Maven依赖项。
无论选择什么选项,请检查它是否支持匿名功能(自Java 8起)。 它可能会自动执行,但仍会检查。
关键是要通过一个非常简单的API来提供此功能,以便您可以避免用户可避免的故障-必须重试几次对外部系统的调用。
翻译自: https://www.javacodegeeks.com/2015/10/retryable-operations.html
新增操作 失败后重试