1

我正在尝试做的事情:
背景:延迟很关键。我有 2 个方法返回基本相同的信息,但执行方式不同。平均而言,一个会更快,但有时可能会更慢。第二个平均速度较慢,但​​更可靠。

所以要求是:

  1. 我有 2 个 CompletableFutures。我想拿第一个回来的。
  2. 当第一个未来发生异常时,我想回退到第二个。
  3. 当第二个未来发生异常时,我想生成异常响应。
  4. 由于#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;
}

对于以下情况,这完全符合预期:

  1. 如果firstFuture先返回,那就是整体结果。
  2. 如果secondFuture先返回,那就是整体结果。
  3. 如果firstFuture遇到异常 (by doOptimisticWork) 然后我们回退到secondFuture.
  4. 如果secondFuture遇到异常,我们直接生成异常响应。
  5. 如果firstFuture超时,那么我们已经返回secondFuture,所以这实际上与 #2 相同。
  6. 如果secondFuture超时,那么我们已经返回firstFuture,所以这实际上与 #1 相同。

这不适用于以下情况:

  1. firstFuture和超时secondFuture。在这种情况下,

我希望compositeFuture异常完成(未来触发这个的一些随机机会,因为它们都有相同的超时)。这会触发 teh 中的异常子句completableFuture,然后等待firstFuture完成。然后firstFuture应该遇到一个超时异常,这会触发它回退到它自己的(secondFuture)异常子句,我们会在该子句中生成一个异常响应。

我添加了一堆 println 来检查每个步骤中每个 CompletableFuture 的状态。实际上发生的是compositeFuture异常完成(如预期),然后我们等待secondFuture完成。then 似乎忽略了自己的secondFuture超时,只是无限期地等待其供应商完成。这是一个问题,因为现在整个序列都在等待这个,因为compositeFuture连接方法被阻塞了。

我对 和 的接近时间持怀疑态度firstFuturesecondFuture但无法通过注入不同的超时来重现任何决定性的行为。有趣的是,我可以通过单元测试确保在do<>Work方法抛出异常时按预期工作,唯一的问题似乎是 CF 配置的超时。

问题:
我们如何将 2 个具有自己超时的 CompleteableFuture 嵌套到一个复合 CompletableFuture 中,并获得遵守超时的整体序列?

4

0 回答 0