重试 HTTP 请求(Java 11 - HttpClient)
Retry HTTP request (Java 11 - HttpClient)
问题
使用 Java 11 中的 HttpClient
(JDK,不是 Apache),如何重试请求?
假设我想重试请求最多 10 次 如果它没有 return 状态代码 200
或抛出异常。
尝试
目前我正在编写 returned 未来循环中的重新安排,我想知道是否有更好或更优雅的方法。
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 需要 return U
而不是 CompletionStage<U>
。
其他框架
为了 QA,我也对显示如何使用其他框架实现此目的的答案感兴趣,但我不会接受它们。
例如,我见过一个名为 Failsafe 的库,它可能会为此提供一个优雅的解决方案(参见 jodah.net/failsafe)。
参考
作为参考,这里有一些相关的Java文档链接:
你可以使用导入
org.eclipse.microprofile.faulttolerance.Retry;
如果您使用 microprofiler 或搜索等效项
@重试
我建议按照这些思路做一些事情(假设没有安全管理器):
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);
}
}
问题
使用 Java 11 中的 HttpClient
(JDK,不是 Apache),如何重试请求?
假设我想重试请求最多 10 次 如果它没有 return 状态代码 200
或抛出异常。
尝试
目前我正在编写 returned 未来循环中的重新安排,我想知道是否有更好或更优雅的方法。
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 需要 return U
而不是 CompletionStage<U>
。
其他框架
为了 QA,我也对显示如何使用其他框架实现此目的的答案感兴趣,但我不会接受它们。
例如,我见过一个名为 Failsafe 的库,它可能会为此提供一个优雅的解决方案(参见 jodah.net/failsafe)。
参考
作为参考,这里有一些相关的Java文档链接:
你可以使用导入 org.eclipse.microprofile.faulttolerance.Retry;
如果您使用 microprofiler 或搜索等效项
@重试
我建议按照这些思路做一些事情(假设没有安全管理器):
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);
}
}