4

问题

使用HttpClientJava 11(JDK,而不是 Apache),如何重试请求?

假设如果请求没有返回状态代码或抛出异常,我想重试最多10 次。200


试图

目前,我正在循环重新安排返回的未来,我想知道是否有更好或更优雅的方式。

CompletableFuture<HttpResponse<Foo>> task = client.sendAsync(request, bodyHandler);

for (int i = 0; i < 10; i++) {
    task = task.thenComposeAsync(response -> response.statusCode() == 200 ?
        CompletableFuture.completedFuture(response) :
        client.sendAsync(request, bodyHandler));
}

// Do something with 'task' ...

如果我们也为特殊情况添加重试,我最终会得到

CompletableFuture<HttpResponse<Foo>> task = client.sendAsync(request, bodyHandler);

for (int i = 0; i < 10; i++) {
    task = task.thenComposeAsync(response ->
            response.statusCode() == 200 ?
            CompletableFuture.completedFuture(response) :
            client.sendAsync(request, bodyHandler))
        .exceptionallyComposeAsync(e ->
            client.sendAsync(request, bodyHandler));
}

// Do something with 'task' ...

不幸的是,似乎没有任何composeAsync触发因素,定期完成和例外。有handleAsync但没有compose,lambda 需要返回U而不是CompletionStage<U>那里。


其他框架

为了质量保证,我也对展示如何使用其他框架实现这一目标的答案感兴趣,但我不会接受它们。

例如,我见过一个名为Failsafe的库,它可能为此提供了一个优雅的解决方案(请参阅jodah.net/failsafe)。


参考

作为参考,这里有一些相关的 JavaDoc 链接:

4

2 回答 2

4

我建议改为按照这些方式做一些事情(假设没有安全管理器):

public static int MAX_RESEND = 10;

public static void main(String[] args) {
    HttpClient client = HttpClient.newHttpClient();
    HttpResponse.BodyHandler<String> handler = HttpResponse.BodyHandlers.ofString();
    HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("http://example.com/")).build();
    var response = client.sendAsync(request, handler)
            .thenComposeAsync(r -> tryResend(client, request, handler, 1, r));
    // do something with response...
}

public static <T> CompletableFuture<HttpResponse<T>>
        tryResend(HttpClient client, HttpRequest request, BodyHandler<T> handler,
                 int count, HttpResponse<T> resp) {
    if (resp.statusCode() == 200 || count >= MAX_RESEND) {
        return CompletableFuture.completedFuture(resp);
    } else {
        return client.sendAsync(request, handler)
                .thenComposeAsync(r -> tryResend(client, request, handler, count+1, r));
    }
}

如果您想同时处理常规和特殊情况,您可以执行以下操作:

public static int MAX_RESEND = 5;

public static void main(String[] args) {
    HttpClient client = HttpClient.newHttpClient();
    BodyHandler<String> handler = BodyHandlers.ofString();
    HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("http://example.com/")).build();
    CompletableFuture<HttpResponse<String>> response = 
        client.sendAsync(request, handler)
            .handleAsync((r, t) -> tryResend(client, request, handler, 1, r, t))
            .thenCompose(Function.identity());
    // do something with response ...
}

public static boolean shouldRetry(HttpResponse<?> r, Throwable t, int count) {
    if (r != null && r.statusCode() == 200 || count >= MAX_RESEND) return false;
    if (t instanceof ... ) return false;
    return true;
}

public static <T> CompletableFuture<HttpResponse<T>>
        tryResend(HttpClient client, HttpRequest request,
                  BodyHandler<T> handler, int count,
                  HttpResponse<T> resp, Throwable t) {
    if (shouldRetry(resp, t, count)) {
        return client.sendAsync(request, handler)
                .handleAsync((r, x) -> tryResend(client, request, handler, count + 1, r, x))
                .thenCompose(Function.identity());
    } else if (t != null) {
        return CompletableFuture.failedFuture(t);
    } else {
        return CompletableFuture.completedFuture(resp);
    }
}
于 2020-07-15T10:47:25.050 回答
0

你可以使用 import org.eclipse.microprofile.faulttolerance.Retry;

如果您使用 microprofiler 或搜索等效项

@重试

于 2020-07-15T10:05:09.743 回答