如何使用 FallbackFuture 来处理 TimeoutException?

How to use FallbackFuture to handle TimeoutException?

我有我的任务和后备方案:

ListenableFuture<T> listenableAsyncTask = executorService.submit(asyncTaskCallable);
ListenableFuture<T> listenableFallbackTask = executorService.submit(fallBackTaskCallable);

从他们那里,我形成了一个容错的ListenableFuture:

ListenableFuture<T> failTolerantListenableFuture = Futures.withFallback(listenableAsyncTask, new FutureFallback<T>() {
                @Override
                public ListenableFuture<T> create(Throwable t) throws Exception {
                    return listenableFallbackTask;
                }
            });

我有一个容错期货列表:

List<ListenableFuture<T>> listenableFutures = ...;

是时候出结果了,在一定的时间内:

result = Futures.allAsList(listenableFutures).get(50,TimeUnit.MILLISECONDS);

此时,我预计如果任务未能在 50 毫秒内完成,return 输出将由轻量级的 fallBackTask 处理。

但与我计划的不同,我遇到了以下异常:

java.util.concurrent.TimeoutException: Timeout waiting for task.

这导致我丢失了其他成功任务的所有结果。在这种情况下,回退似乎对我不起作用。或者我误解了这个概念?

我们需要区分"the Future fails"和"the call to Future.get fails."

  • "The Future fails"如果你提交的任务抛出异常。 (出于 withFallback 的目的,我们也将取消视为失败。但这与此无关,并且行为可能有一天会改变。)
  • "The call to Future.get fails" 如果 任何 发生以下情况:
    • Future 失败
    • 通话超时
    • 通话中断

withFallback只处理Future失败的情况,不处理超时或中断的情况。

如果你的目标是检索所有在 50 毫秒内完成的主要结果,所有其他情况回落到次要结果,你可以尝试这样的事情,它使用 withTimeout 自动失败给定超时后的 Future

List<ListenableFuture<T>> originalFutures = ...;
List<ListenableFuture<T>> defaultAfterTimeoutFutures = new ArrayList<>();
for (ListenableFuture<T> f : originalFutures) {
  f = Futures.withTimeout(f, 50, MILLISECONDS, executor);
  f = Futures.withFallback(f, ...);
  defaultAfterTimeoutFutures.add(f);
}
result = Futures.allAsList(defaultAfterTimeoutFutures).get();

但请注意,最后一个 get 调用可能会等待超过 50 毫秒:如果主 Future 失败,则 get 调用必须等到其回退完成。如果您不想等待回退,那么您还需要用 withTimeout 将它们包装起来。如果你确实包装它们,那么它们将在超时后失败,此时 allAsList 也会失败。如果你不想要那个,那么你需要使用 successfulAsList(而不是 allAsList)或者再次用 withFallback 包装包装器,这次的值总是立即可用。