如果任何服务失败,如果使用 Completable future 调用多个服务,则停止其他服务调用

stop other service calls if using Completable future to call multiple services if any of service fails

我正在使用可完成的未来来调用多个服务调用。

CompletableFuture<Response1> future1 = CompletableFuture.supplyAsync(() -> webServiceCall1());
CompletableFuture<Response2> future2 = CompletableFuture.supplyAsync(() -> webServiceCall2());
CompletableFuture<Response3> future3 = CompletableFuture.supplyAsync(() -> webServiceCall3());

然后我正在使用:

Response1 response1 = future1.get();
Response2 response2 = future2.get();
Response3 response3 = future3.get();
// do further processing on the response

为什么我要使用这种代码???通过这样做,我能够提高性能。如果 service1 以 200 次成功并花费 3 秒的时间,service2 以 200 次成功并花费 3 秒的时间,service3 以 200 次成功并花费 3 秒的时间,因此依次调用它们将花费 9 秒的时间。但是,如果我使用上面写的内容,所有这些都将并行执行并且将在 3 秒内完成。

现在的问题是,如果 webservice1 失败,则不需要访问 webservice2 和 webservice3。但在我的场景中,由于代码是以调用 webservice2 和 webservice3 的方式编写的,即使 webservice1 失败也是如此。

我们如何才能避免这种情况并仍然保持并行 web 服务调用并且同时仍然保持故障安全??

让我换个说法——我们如何才能保持这段代码的整体性能(考虑大约 3 秒左右的基准测试)并且同时仍然保持故障安全??

您可以使用 thenApplyAsync 函数:

ResponseHolder holder = CompletableFuture.supplyAsync(ResponseHolder::new)
  .thenApplyAsync(h -> h.setResponse1(webServiceCall1()))
  .thenApplyAsync(h -> h.setResponse2(webServiceCall2()))
  .thenApplyAsync(h -> h.setResponse3(webServiceCall3()))
  .get() // may fail
;

ResponseHolder class:

public class ResponseHolder {
  private Response1 response1;
  private Response2 response2;
  private Response3 response3;
  // for each responseX:
  public ResponseHolder setResponse1(Response1 response1) {
    this.response1 = response1;
    return this;
  }
}

How we can avoid this & still keep webservice calls parallel & still fail safe at the same time ??

你不能。

当您听到服务调用 1 失败(例如)时,您已经已经开始处理 Web 服务调用 2 和 3。不打电话给他们已经太晚了。

如果你必须在服务调用1成功后才调用服务2,那么你必须等到知道服务调用1已经成功。

否则你是在求一个算命先生:现在调用服务 2 和 3,如果服务 1 在未来某个时间成功。

你能做的最好的事情是提供一种方法来取消正在进行的服务调用(即告诉他们在下一个方便的时间点退出),但这可能比获得更多的麻烦更值得对。

  • webServiceCall1()无论如何都会开火
  • webServiceCall2() 将检查 webServiceCall1()
    • 检查是否已返回 1 的响应状态
      • 如果响应状态为 200,则继续并触发
      • 如果响应状态不是200,请勿拨打电话
      • 如果尚未返回响应状态,请继续进行调用 - 本质上,我们假设它会成功
  • webServiceCall3 将检查 webServiceCall1()webServiceCall2()

我想强调一下 - 如果在我们尝试 webServiceCall2()webServiceCall1() 尚未完成,那么我们 假设 webServiceCall1() 已返回并成功返回 200 响应代码。如果它最终 webServiceCall1()not 即使在假设它成功之后,那么我们将毫无意义地调用 webServiceCall2()(并且可能 webServiceCall3()),所以请注意这一点。

我的解决方案 ONLY 涵盖了 webServiceCall1() 触发和失败的边缘情况,我们很快就收到了回复在我们有机会在第二个 CompletableFuture 中检查它之前。 webServiceCall2() 和第三个 CompletableFuture 相同。在这两种情况下,我们都会优化掉 1 或 2 个调用。但是不要假设这会发生每次webServiceCall1()webServiceCall2()失败。

CompletableFuture<Response1> future1 = CompletableFuture.supplyAsync(() -> webServiceCall1());

CompletableFuture<Response2> future2 = CompletableFuture.supplyAsync(() -> (performServiceCheck(future1) ? webServiceCall2() : null));

CompletableFuture<Response3> future3 = CompletableFuture.supplyAsync(() -> (performServiceCheck(future1, future2) ? webServiceCall3() : null));
private boolean performServiceCheck(CompletableFuture<?> ...futures) {
    
    boolean result = true;
    
    for (CompletableFuture<?> future : futures) {
        if (future.isDone() && is200(future.get())) {
            result = result && true;
        } else if (future.isDone() && !is200(future.get())) {
            result = result && false;
        } else if (!future.isDone()) {
            result = result && true;
        }
    }
    
    return result;
    
}