链接后续重试可以很简单:
public CompletableFuture<Result> executeActionAsync() {
CompletableFuture<Result> f=executeMycustomActionHere();
for(int i=0; i<MAX_RETRIES; i++) {
f=f.exceptionally(t -> executeMycustomActionHere().join());
}
return f;
}
阅读下面的
缺点 这只会按预期链接尽可能多的重试,因为这些后续阶段在非特殊情况下不会执行任何操作。
一个缺点是,如果第一次尝试立即失败,因此在第一个处理程序链接时已经异常完成,则调用线程将调用该操作,从而完全删除请求的异步性质。通常,可能会阻塞线程(默认执行器将启动新的补偿线程,但仍然不鼓励这样做)。不幸的是,两者都没有,也没有方法。f
exceptionally
join()
exceptionallyAsync
exceptionallyCompose
不调用的解决方案是join()
public CompletableFuture<Result> executeActionAsync() {
CompletableFuture<Result> f=executeMycustomActionHere();
for(int i=0; i<MAX_RETRIES; i++) {
f=f.thenApply(CompletableFuture::completedFuture)
.exceptionally(t -> executeMycustomActionHere())
.thenCompose(Function.identity());
}
return f;
}
演示如何组合“compose”和“异常”处理程序。
此外,如果所有重试都失败,则仅报告最后一个异常。更好的解决方案应报告第一个异常,并将重试的后续异常添加为抑制异常。这样的解决方案可以通过链接递归调用来构建,正如Gili的答案所暗示的那样,但是,为了将这个想法用于异常处理,我们必须使用以下步骤来组合“compose”和“exceptionally”,如上所示:
public CompletableFuture<Result> executeActionAsync() {
return executeMycustomActionHere()
.thenApply(CompletableFuture::completedFuture)
.exceptionally(t -> retry(t, 0))
.thenCompose(Function.identity());
}
private CompletableFuture<Result> retry(Throwable first, int retry) {
if(retry >= MAX_RETRIES) return CompletableFuture.failedFuture(first);
return executeMycustomActionHere()
.thenApply(CompletableFuture::completedFuture)
.exceptionally(t -> { first.addSuppressed(t); return retry(first, retry+1); })
.thenCompose(Function.identity());
}
CompletableFuture.failedFuture
是一个 Java 9 方法,但如果需要,将兼容 Java 8 的向后移植添加到代码中是微不足道的:
public static <T> CompletableFuture<T> failedFuture(Throwable t) {
final CompletableFuture<T> cf = new CompletableFuture<>();
cf.completeExceptionally(t);
return cf;
}