中断CompletableFuture,默认值为

时间:2017-10-20 14:25:06

标签: java java-8 completable-future

假设我有3项服务。 首先,我致电serviceA,它会返回CompletableFuture。 在结果之后,我调用了serviceBserviceC paralelly(thenCompose())。 在我得到所有结果后,我想将所有3个结果合并并将其返回给某个调用者。 在调用者中,我想等待整个过程整整X毫秒,以便:

  • 如果我在serviceA调用正在进行时中断该过程:抛出一些异常(因此必须这样做)
  • 如果我在serviceBserviceC调用正在进行时中断该过程:返回一些默认值(它们是可选的)。 这就是我尝试使用getNow(fallback)
  • CompletableFuture方法的原因

请检查我的代码段下方,如果我在serviceBserviceC来电中使用长时间延迟,我总是会以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;
}

1 个答案:

答案 0 :(得分:2)

.allOf(resultB, resultC)返回的未来只有在完成resultBresultC时才会完成,因此,只有{{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()