我必须每天多次通过网络调用关键API(这使它不可靠)。如果API无法响应(超时)或返回错误(因为服务器依次调用其他不可靠的第三方),则我必须通知他们有关错误,然后再次调用原始API。
这里,错误通知也可能会失败,因此我必须重试通知错误,直到我成功。之后,我可以再次调用原始API。
我想使用CompletableFuture进行构图。对于这种代码,库是正确的选择吗?还是我应该将try catch循环放在一个包装在Runnable中的无限循环中并执行它? CompletableFuture是否会过大?还有其他选择吗?
坦率地说,这个问题太笼统了,因为答案实际上取决于您已经在使用什么框架。问题是-当今有许多流行的框架提供开箱即用的重试逻辑的实现。
例如:
如果您是通过伪装来调用“关键API”,那么会有built-in support for retry。
[如果您使用的是Spring的RestTemplate
,那么您也可以使用Spring Retry
如果您只需要简单的重试实用程序,则有许多库,例如failsafe和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;
}