我有一个拥有以下签名的方法:
public Optional<String> doSomething() {
...
}
如果我得到一个空的Optional
,我想重试这个方法,并在重试了3次后才返回空的Optional
。我查找并找到了
Retryable
Spring注释,但它似乎只适用于异常。如果可能的话,我想使用一个库来实现这个功能,并避免:
- 创建和抛出异常。
- 自己编写逻辑。
我有一个拥有以下签名的方法:
public Optional<String> doSomething() {
...
}
如果我得到一个空的Optional
,我想重试这个方法,并在重试了3次后才返回空的Optional
。Retryable
Spring注释,但它似乎只适用于异常。我一直在使用内置重试的 failsafe。你可以基于谓词和异常来重试。
您的代码将如下所示:
private Optional<String> doSomethingWithRetry() {
RetryPolicy<Optional> retryPolicy = new RetryPolicy<Optional>()
.withMaxAttempts(3)
.handleResultIf(result -> {
System.out.println("predicate");
return !result.isPresent();
});
return Failsafe
.with(retryPolicy)
.onSuccess(response -> System.out.println("ok"))
.onFailure(response -> System.out.println("no ok"))
.get(() -> doSomething());
}
private Optional<String> doSomething() {
return Optional.of("result");
}
predicate
ok
predicate
predicate
predicate
no ok
@Retryable
(以及基础的RetryTemplate
)完全基于异常。
您可以子类化RetryTemplate
,覆盖doExecute()
以检查返回值。
您可能需要复制方法中的大部分代码;它实际上并不是为了仅覆盖retryCallback.doWithRetry()
调用而设计的。
您可以在@Retryable
的interceptor
属性中指定一个自定义的RetryOperationsInterceptor
来使用自定义的RetryTemplate
。
编辑
当前的RetryTemplate
代码如下...
while (canRetry(retryPolicy, context) && !context.isExhaustedOnly()) {
try {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Retry: count=" + context.getRetryCount());
}
// Reset the last exception, so if we are successful
// the close interceptors will not think we failed...
lastException = null;
return retryCallback.doWithRetry(context);
}
catch (Throwable e) {
lastException = e;
try {
registerThrowable(retryPolicy, state, context, e);
}
catch (Exception ex) {
throw new TerminatedRetryException("Could not register throwable",
ex);
}
finally {
doOnErrorInterceptors(retryCallback, context, e);
}
...
}
while (canRetry(retryPolicy, context) && !context.isExhaustedOnly()) {
try {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Retry: count=" + context.getRetryCount());
}
// Reset the last exception, so if we are successful
// the close interceptors will not think we failed...
lastException = null;
T result = retryCallback.doWithRetry(context);
if (((Optional<String>) result).get() == null) {
try {
registerThrowable(retryPolicy, state, context, someDummyException);
}
catch (Exception ex) {
throw new TerminatedRetryException("Could not register throwable",
ex);
}
finally {
doOnErrorInterceptors(retryCallback, context, e);
}
...
}
else {
return result;
}
}
catch (Throwable e) {
...
}
其中someDummyException
只是为了欺骗上下文而增加计数器。它可以是一个static
字段,仅创建一次即可。
我目前已经自己编写了一个工具(使用纯Java),当然欢迎其他答案:
Original Answer翻译成"最初的回答"
import java.util.function.Predicate;
import java.util.function.Supplier;
public class Retryable<T> {
private Supplier<T> action = () -> null;
private Predicate<T> successCondition = ($) -> true;
private int numberOfTries = 3;
private long delay = 1000L;
private Supplier<T> fallback = () -> null;
public static <A> Retryable<A> of(Supplier<A> action) {
return new Retryable<A>().run(action);
}
public Retryable<T> run(Supplier<T> action) {
this.action = action;
return this;
}
public Retryable<T> successIs(Predicate<T> successCondition) {
this.successCondition = successCondition;
return this;
}
public Retryable<T> retries(int numberOfTries) {
this.numberOfTries = numberOfTries;
return this;
}
public Retryable<T> delay(long delay) {
this.delay = delay;
return this;
}
public Retryable<T> orElse(Supplier<T> fallback) {
this.fallback = fallback;
return this;
}
public T execute() {
for (int i = 0; i < numberOfTries; i++) {
T t = action.get();
if (successCondition.test(t)) {
return t;
}
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
// do nothing
}
}
return fallback.get();
}
}
:
public Optional<String> doSomething() {
return Retryable
.of(() -> actualDoSomething())
.successIs(Optional::isPresent)
.retries(3)
.delay(1000L)
.orElse(Optional::empty)
.execute();
}
如果你的结果不符合预期,就抛出异常
Optional
呢?我的意思是,既然@Retryable
可以处理抛出的异常,你完全可以自己创建一个异常,在未获取到值的情况下抛出它,然后重试操作。这样你就可以节省一些时间,不用编写自己的注释等内容了。 - akortex