我正在尝试做的事情:
背景:延迟很关键。我有 2 个方法返回基本相同的信息,但执行方式不同。平均而言,一个会更快,但有时可能会更慢。第二个平均速度较慢,但更可靠。
所以要求是:
- 我有 2 个 CompletableFutures。我想拿第一个回来的。
- 当第一个未来发生异常时,我想回退到第二个。
- 当第二个未来发生异常时,我想生成异常响应。
- 由于#2 和#3,如果两者都发生异常,我想生成异常响应。
现有代码尝试:
public Foo getFoo() {
CompletableFuture<Foo> firstFuture = CompletableFuture
.supplyAsync(this::doOptimisticWork, this.executor)
.timeout(this.timeout, TimeUnit.MILLISECONDS));
CompletableFuture<Foo> secondFuture = CompletableFuture
.supplyAsync(this::doFallbackWork, this.executor)
.timeout(this.timeout, TimeUnit.MILLISECONDS))
.exceptionally(this::getExceptionalResponse);
CompletableFuture<Foo> compositeFuture = CompletableFuture
.anyOf(firstFuture, secondFuture)
.exceptionally(throwable -> secondFuture.get())
.thenApply(Foo.class::cast);
return compositeFuture.join;
}
对于以下情况,这完全符合预期:
- 如果
firstFuture
先返回,那就是整体结果。 - 如果
secondFuture
先返回,那就是整体结果。 - 如果
firstFuture
遇到异常 (bydoOptimisticWork
) 然后我们回退到secondFuture
. - 如果
secondFuture
遇到异常,我们直接生成异常响应。 - 如果
firstFuture
超时,那么我们已经返回secondFuture
,所以这实际上与 #2 相同。 - 如果
secondFuture
超时,那么我们已经返回firstFuture
,所以这实际上与 #1 相同。
这不适用于以下情况:
firstFuture
和超时secondFuture
。在这种情况下,
我希望compositeFuture
异常完成(未来触发这个的一些随机机会,因为它们都有相同的超时)。这会触发 teh 中的异常子句completableFuture
,然后等待firstFuture
完成。然后firstFuture
应该遇到一个超时异常,这会触发它回退到它自己的(secondFuture)异常子句,我们会在该子句中生成一个异常响应。
我添加了一堆 println 来检查每个步骤中每个 CompletableFuture 的状态。实际上发生的是compositeFuture
异常完成(如预期),然后我们等待secondFuture
完成。then 似乎忽略了自己的secondFuture
超时,只是无限期地等待其供应商完成。这是一个问题,因为现在整个序列都在等待这个,因为compositeFuture
连接方法被阻塞了。
我对 和 的接近时间持怀疑态度firstFuture
,secondFuture
但无法通过注入不同的超时来重现任何决定性的行为。有趣的是,我可以通过单元测试确保在do<>Work
方法抛出异常时按预期工作,唯一的问题似乎是 CF 配置的超时。
问题:
我们如何将 2 个具有自己超时的 CompleteableFuture 嵌套到一个复合 CompletableFuture 中,并获得遵守超时的整体序列?