我希望 HttpClient 5 在遇到 TunnelRefusedException 时重试请求(代理拒绝初始连接是随机发生的,但重试时通常可以正常工作)。
我尝试扩展DefaultHttpRequestRetryStrategy,但它只能捕获继承自
IOException
的异常:
public class MyRetryStrategy extends DefaultHttpRequestRetryStrategy {
public MyRetryStrategy() {
super(2,
TimeValue.ofSeconds(1L),
Arrays.asList(InterruptedIOException.class, UnknownHostException.class, ConnectException.class, ConnectionClosedException.class, NoRouteToHostException.class, SSLException.class,
TunnelRefusedException.class // not valid, because it is not an IOException !
),
Arrays.asList(429, 503)
);
}
}
那么,也许
HttpRequestRetryStrategy
界面应该支持任何类型的Exception
?或者也许 TunnelRefusedException
应该是 IOException
?
关于如何做到这一点有什么想法吗?
我想重试的错误的示例堆栈跟踪:
org.apache.hc.client5.http.ClientProtocolException: CONNECT refused by proxy: HTTP/1.1 500 Internal Server Error
at org.apache.hc.client5.http.impl.classic.InternalHttpClient.doExecute(InternalHttpClient.java:173)
at org.apache.hc.client5.http.impl.classic.CloseableHttpClient.execute(CloseableHttpClient.java:245)
at org.apache.hc.client5.http.impl.classic.CloseableHttpClient.execute(CloseableHttpClient.java:188)
[...]
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: org.apache.hc.client5.http.impl.TunnelRefusedException: CONNECT refused by proxy: HTTP/1.1 500 Internal Server Error
at org.apache.hc.client5.http.impl.classic.ConnectExec.createTunnelToTarget(ConnectExec.java:284)
at org.apache.hc.client5.http.impl.classic.ConnectExec.execute(ConnectExec.java:151)
at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
at org.apache.hc.client5.http.impl.classic.ProtocolExec.execute(ProtocolExec.java:192)
at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
at org.apache.hc.client5.http.impl.classic.HttpRequestRetryExec.execute(HttpRequestRetryExec.java:96)
at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
at org.apache.hc.client5.http.impl.classic.ContentCompressionExec.execute(ContentCompressionExec.java:152)
at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
at org.apache.hc.client5.http.impl.classic.RedirectExec.execute(RedirectExec.java:115)
at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
at org.apache.hc.client5.http.impl.classic.InternalHttpClient.doExecute(InternalHttpClient.java:170)
... 19 more
DefaultHttpRequestRetryStrategy已在定义中排除异常列表,请参阅它们此处
Arrays.asList(InterruptedIOException.class,UnknownHostException.class,ConnectException.class,ConnectionClosedException.class,NoRouteToHostException.class,SSLException.class)
最简单的方法是实施自定义策略并开始添加例外情况。
public class Main {
public static void main(String[] args) throws IOException {
final ClassicHttpRequest httpGet = ClassicRequestBuilder.get("https://httpbin.org/status/500")
.build();
CloseableHttpClient httpClient = HttpClientBuilder
.create()
.setRetryStrategy(new CustomRetryStrategy(3, TimeValue.ofSeconds(3)))
.build();
httpClient.execute(httpGet, response -> {
System.out.println(response.getCode() + " " + response.getReasonPhrase());
return null;
});
}
@Slf4j
static class CustomRetryStrategy implements HttpRequestRetryStrategy {
private final int maxRetries;
private final TimeValue retryInterval;
public CustomRetryStrategy(final int maxRetries, final TimeValue retryInterval) {
this.maxRetries = maxRetries;
this.retryInterval = retryInterval;
}
@Override
public boolean retryRequest(
final HttpRequest request,
final IOException exception,
final int execCount,
final HttpContext context) {
Args.notNull(request, "request");
Args.notNull(exception, "exception");
if (execCount > this.maxRetries) {
// Do not retry if over max retries
return false;
}
return true;
}
@Override
public boolean retryRequest(
final HttpResponse response,
final int execCount,
final HttpContext context) {
Args.notNull(response, "response");
return execCount <= this.maxRetries;
}
@Override
public TimeValue getRetryInterval(HttpResponse response, int execCount, HttpContext context) {
log.warn("Retrying HTTP request after " + retryInterval.toString());
return retryInterval;
}
}
}
我的本地测试输出如下所示
23:18:03: Executing ':Main.main()'...
> Task :compileJava
> Task :processResources NO-SOURCE
> Task :classes
> Task :Main.main()
[main] WARN io.github.ozkanpakdil.Main$CustomRetryStrategy - Retrying HTTP request after 3 SECONDS
[main] WARN io.github.ozkanpakdil.Main$CustomRetryStrategy - Retrying HTTP request after 3 SECONDS
[main] WARN io.github.ozkanpakdil.Main$CustomRetryStrategy - Retrying HTTP request after 3 SECONDS
500 INTERNAL SERVER ERROR
Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.
You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.
For more on this, please refer to https://docs.gradle.org/8.5/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.
BUILD SUCCESSFUL in 11s
2 actionable tasks: 2 executed
23:18:15: Execution finished ':Main.main()'.