假设我有3项服务。
首先,我致电serviceA
,它会返回CompletableFuture
。
在结果之后,我调用了serviceB
和serviceC
paralelly(thenCompose()
)。
在我得到所有结果后,我想将所有3个结果合并并将其返回给某个调用者。
在调用者中,我想等待整个过程整整X毫秒,以便:
serviceA
调用正在进行时中断该过程:抛出一些异常(因此必须这样做)serviceB
和serviceC
调用正在进行时中断该过程:返回一些默认值(它们是可选的)。
这就是我尝试使用getNow(fallback)
CompletableFuture
方法的原因
请检查我的代码段下方,如果我在serviceB
和serviceC
来电中使用长时间延迟,我总是会以TimeoutException
结尾。
我怎么能这样做?
public CompletableFuture<Result> getFuture() {
CompletableFuture<A> resultA = serviceA.call();
CompletableFuture<B> resultB = resultA.thenCompose(a -> serviceB.call(a));
CompletableFuture<C> resultC = resultA.thenCompose(a -> serviceC.call(a));
return CompletableFuture.allOf(resultB, resultC)
.thenApply(ignoredVoid -> combine(
resultA.join(),
resultB.getNow(fallbackB),
resultC.getNow(fallbackC));
}
public Result extractFuture(CompletableFuture<Result> future) {
Result result;
try {
result = future.get(timeOut, MILLISECONDS);
} catch (ExecutionException ex) {
...
} catch (InterruptedException | TimeoutException ex) {
// I always ends up here...
}
return result;
}
答案 0 :(得分:2)
.allOf(resultB, resultC)
返回的未来只有在完成resultB
和resultC
时才会完成,因此,只有{{1}才能评估从属函数ignoredVoid -> combine(resultA.join(), resultB.getNow(fallbackB), resultC.getNow(fallbackC)
}}和resultB
已完成,并且提供后备功能根本没有效果。
通常无法对这些功能中的resultC
电话做出反应。考虑到在不同时间以不同的超时可以对未来进行任意数量的get()
调用,这应该是显而易见的,但传递给get()
的函数仅被评估一次。
在thenApply
内处理消费者指定超时的唯一方法是更改它以返回接收超时的函数:
getFuture()
现在,可以进行具有不同超时的不同呼叫,只要B或C尚未完成,可能会产生不同的结果。并不是说interface FutureFunc<R> {
R get(long time, TimeUnit u) throws ExecutionException;
}
public FutureFunc<Result> getFuture() {
CompletableFuture<A> resultA = serviceA.call();
CompletableFuture<B> resultB = resultA.thenCompose(a -> serviceB.call(a));
CompletableFuture<C> resultC = resultA.thenCompose(a -> serviceC.call(a));
CompletableFuture<Result> optimistic = CompletableFuture.allOf(resultB, resultC)
.thenApply(ignoredVoid -> combine(resultA.join(), resultB.join(), resultC.join()));
return (t,u) -> {
try {
return optimistic.get(t, u);
} catch (InterruptedException | TimeoutException ex) {
return combine(resultA.join(), resultB.getNow(fallbackB),
resultC.getNow(fallbackC));
}
};
}
public Result extractFuture(FutureFunc<Result> future) {
Result result;
try {
result = future.get(timeOut, MILLISECONDS);
} catch (ExecutionException ex) {
...
}
return result;
}
方法存在一些歧义,这可能还需要一些时间。
您可以将功能更改为
combine
等待可能已经运行return (t,u) -> {
try {
if(resultB.isDone() && resultC.isDone()) return optimistic.get();
return optimistic.get(t, u);
} catch (InterruptedException | TimeoutException ex) {
return combine(resultA.join(), resultB.getNow(fallbackB),
resultC.getNow(fallbackC));
}
};
的完成。在任何一种情况下,都没有保证结果在指定时间内传递,因为即使使用B和C的回退值,也会执行combine
,这可能需要任意时间
如果您想要取消行为,即所有结果查询返回相同的结果,即使它是使用第一个查询引起的回退值计算的,您也可以使用
combine
这样,单个public FutureFunc<Result> getFuture() {
CompletableFuture<A> resultA = serviceA.call();
CompletableFuture<B> resultB = resultA.thenCompose(a -> serviceB.call(a));
CompletableFuture<C> resultC = resultA.thenCompose(a -> serviceC.call(a));
CompletableFuture<Void> bAndC = CompletableFuture.allOf(resultB, resultC);
CompletableFuture<Result> result = bAndC
.thenApply(ignoredVoid -> combine(resultA.join(), resultB.join(),
resultC.join()));
return (t,u) -> {
try {
bAndC.get(t, u);
} catch (InterruptedException|TimeoutException ex) {
resultB.complete(fallbackB);
resultC.complete(fallbackC);
}
try {
return result.get();
} catch (InterruptedException ex) {
throw new ExecutionException(ex);
}
};
}
上的所有查询都将始终返回相同的结果,即使它是基于第一次超时引起的回退值。此变体也始终排除从超时执行FutureFunc
。
当然,如果根本不打算进行不同的超时,您可以重构combine
以提前获得所需的超时,例如作为参数。这将大大简化实施,并可能再次回归未来:
getFuture()