如何重复调用不可靠的 API? Completable Future 会比在线程中编写标准的 try catch 块 运行 有用吗?
How to call an unreliable API repeatedly? Will Completable Future be helpful than writing standard try catch block running in a thread?
我必须一次通过网络多次呼叫关键 API(这使得它不可靠),一天多次。如果 API 无法响应(超时)或 returns 错误(因为该服务器反过来调用其他不可靠的第三方)我必须通知他们有关错误,然后调用原始 API 再次.
在这里,错误通知也可能会失败,我将不得不重试通知错误,直到我成功。之后我可以再次调用原始 API。
我想使用 CompletableFuture 构建它。对于这种代码,它是一个正确的库选择吗?或者我应该把一个 try catch 循环放在一个包含在 Runnable 中的无限循环中并执行它? CompletableFuture 会矫枉过正吗?还有其他选择吗?
坦率地说,这个问题太宽泛了,因为答案实际上取决于您已经在使用什么框架。
问题是 - 当今有许多流行的框架提供开箱即用的重试逻辑实现。
例如:
如果你通过伪装调用 "critical API",那么会有一个 built-in support for retry.
如果您使用 Spring 的 RestTemplate
,那么您也可以使用 Spring Retry
如果您只需要简单的重试实用程序,有许多库,例如 failsafe and guava-retrying
但是,如果在考虑替代方案后您决定使用 CompletableFuture
手动重试,您绝对可以这样做。
例如,以下是重试的简单实用程序:
- 不限次数:
public static <T> CompletableFuture<T> retry(Throwable throwable, Function<Throwable, T> operation) {
return CompletableFuture.supplyAsync(() -> operation.apply(throwable))
.thenApply(CompletableFuture::completedFuture)
.exceptionally(error -> retry(error, operation))
.thenCompose(Function.identity());
}
注意:这里我假设,你可以根据Throwable
的实例做出一些决定。因此,operation
将错误作为输入并产生一些有用的结果作为输出。在您的情况下,错误 - 可能是 critical API
调用错误,或 notification error
,有用的结果 - 成功 critical API
结果。
- 固定次数(本例为
MAX_RETRIES
):
public static <T> CompletableFuture<T> retry(Throwable throwable, Function<Throwable, T> operation, int retry) {
if (retry == MAX_RETRIES) return failedFuture(throwable);
return CompletableFuture.supplyAsync(() -> operation.apply(throwable))
.thenApply(CompletableFuture::completedFuture)
.exceptionally(error -> retry(error, operation, retry + 1))
.thenCompose(Function.identity());
}
public static <T> CompletableFuture<T> failedFuture(Throwable throwable) {
final CompletableFuture<T> failedFuture = new CompletableFuture<>();
failedFuture.completeExceptionally(throwable);
return failedFuture;
}
我必须一次通过网络多次呼叫关键 API(这使得它不可靠),一天多次。如果 API 无法响应(超时)或 returns 错误(因为该服务器反过来调用其他不可靠的第三方)我必须通知他们有关错误,然后调用原始 API 再次.
在这里,错误通知也可能会失败,我将不得不重试通知错误,直到我成功。之后我可以再次调用原始 API。
我想使用 CompletableFuture 构建它。对于这种代码,它是一个正确的库选择吗?或者我应该把一个 try catch 循环放在一个包含在 Runnable 中的无限循环中并执行它? CompletableFuture 会矫枉过正吗?还有其他选择吗?
坦率地说,这个问题太宽泛了,因为答案实际上取决于您已经在使用什么框架。 问题是 - 当今有许多流行的框架提供开箱即用的重试逻辑实现。
例如:
如果你通过伪装调用 "critical API",那么会有一个 built-in support for retry.
如果您使用 Spring 的
RestTemplate
,那么您也可以使用 Spring Retry如果您只需要简单的重试实用程序,有许多库,例如 failsafe and guava-retrying
但是,如果在考虑替代方案后您决定使用 CompletableFuture
手动重试,您绝对可以这样做。
例如,以下是重试的简单实用程序:
- 不限次数:
public static <T> CompletableFuture<T> retry(Throwable throwable, Function<Throwable, T> operation) {
return CompletableFuture.supplyAsync(() -> operation.apply(throwable))
.thenApply(CompletableFuture::completedFuture)
.exceptionally(error -> retry(error, operation))
.thenCompose(Function.identity());
}
注意:这里我假设,你可以根据Throwable
的实例做出一些决定。因此,operation
将错误作为输入并产生一些有用的结果作为输出。在您的情况下,错误 - 可能是 critical API
调用错误,或 notification error
,有用的结果 - 成功 critical API
结果。
- 固定次数(本例为
MAX_RETRIES
):
public static <T> CompletableFuture<T> retry(Throwable throwable, Function<Throwable, T> operation, int retry) {
if (retry == MAX_RETRIES) return failedFuture(throwable);
return CompletableFuture.supplyAsync(() -> operation.apply(throwable))
.thenApply(CompletableFuture::completedFuture)
.exceptionally(error -> retry(error, operation, retry + 1))
.thenCompose(Function.identity());
}
public static <T> CompletableFuture<T> failedFuture(Throwable throwable) {
final CompletableFuture<T> failedFuture = new CompletableFuture<>();
failedFuture.completeExceptionally(throwable);
return failedFuture;
}