Apache HttpClient请求重试之间的超时时间

5

有人能分享如何配置现代化的HttpClient 4.5.3以在每次重试之前等待一段时间并重试失败的请求吗?

目前看来,我已经正确地理解了.setRetryHandler(new DefaultHttpRequestRetryHandler(X, false))将允许重试X次请求。

但我无法理解如何配置退避:.setConnectionBackoffStrategy() / .setBackoffManager()根据JavaDocs调节其他内容,而不是重试之间的超时。


也许这个链接会有帮助:https://dev59.com/55Tfa4cB1Zd3GeqPLhAH? - diginoise
2
@diginoise,您所提到的ExponentialBackOffSchedulingStrategy与异步缓存重新验证有关,而不是请求执行。 - ok2c
@snowindy,您想在I/O错误或特定状态码时重试请求吗? - ok2c
@oleg 好的,我会为所有IO异常和可能的一些代码重试。DefaultBackoffStrategy 的逻辑基本上符合我的需求。 - snowindy
4个回答

10

关于动态延迟,我建议这样:

CloseableHttpClient client = HttpClientBuilder.create()
    .setRetryHandler(new HttpRequestRetryHandler() {
        @Override
        public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
            return executionCount <= maxRetries ;
        }
    })
    .setServiceUnavailableRetryStrategy(new ServiceUnavailableRetryStrategy() {
        int waitPeriod = 100;
        @Override
        public boolean retryRequest(HttpResponse response, int executionCount, HttpContext context) {
            waitPeriod *= 2;
            return executionCount <= maxRetries &&
               response.getStatusLine().getStatusCode() >= 500; //important!
        }

        @Override
        public long getRetryInterval() {
            return waitPeriod;
        }
    })
    .build();

附录: 请注意,如果出现超时、端口未开放或连接关闭等IO错误,将不会调用ServiceUnavailableRetryStrategy.retryRequest。在这种情况下,只会调用HttpRequestRetryHandler.retryRequest,并且重试将立即发生或在固定延迟后发生(我最终无法澄清这一点)。因此,Oleg的答案实际上是正确的。没有办法使用HttpClient 4.5支持此功能。

(实际上,我认为这是一个设计缺陷,在现代微服务环境中,IO错误后的延迟重试非常重要。)


等待时间是以毫秒为单位吗? - Mitaksh Gupta

8
BackoffManager / ConnectionBackoffStrategy 组合可用于根据 I/O 错误和 503 响应的速率动态增加或减少每路最大连接限制。它们不会影响请求执行,并且不能用于控制请求重新执行。这是使用 HC 4.x API 的最佳方法。
CloseableHttpClient client = HttpClientBuilder.create()
        .setRetryHandler(new HttpRequestRetryHandler() {
            @Override
            public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
                return executionCount <= maxRetries &&
                        exception instanceof SocketException;
            }
        })
        .setServiceUnavailableRetryStrategy(new ServiceUnavailableRetryStrategy() {
            @Override
            public boolean retryRequest(HttpResponse response, int executionCount, HttpContext context) {
                return executionCount <= maxRetries &&
                        response.getStatusLine().getStatusCode() == HttpStatus.SC_SERVICE_UNAVAILABLE;
            }

            @Override
            public long getRetryInterval() {
                return 100;
            }
        })
        .build();

请注意,目前没有一种优雅的方式来强制执行请求执行尝试之间的延迟,以防止I/O错误或根据请求路由动态调整重试间隔。

关于您的结束语,您可以添加一个 HttpRequestRetryHandler 的实现,并覆盖 retryRequest(...) 方法以添加延迟,甚至是动态或指数级的,用于处理 IOException - Drakes
重试间隔是以毫秒为单位吗? - Mitaksh Gupta

2

您可以使用 Lambda

client.setRetryHandler((e, execCount, httpContext) -> {
                if (execCount > tries) {
                    return false;
                } else {
                    try {
                        Thread.sleep(recalMillis);
                    } catch (InterruptedException ex) {
                        //ignore
                    }
                    return true;
                }

请注意,处理程序仅适用于IOException类型的异常。


或者,没有lambda表达式,使用.setRetryHandler(new HttpRequestRetryHandler() { .. })并覆盖retryRequest()。这是一个很好的解决方案,因为重试和延迟都在此手动编码。 - gene b.

0

这对我们非常有效。它可以进行重试(3次)和延迟(1000ms)。我们覆盖了两个东西:HttpResponseInterceptor.process()HttpRequestRetryHandler.retryRequest()。第一个会对无效的(400+)HTTP状态码抛出异常,然后进入retryRequest实现。注意:如果所有的重试都已经用尽,您将来到此片段底部的最终Catch。

// Define an HttpClient with the following features:
// 1) Intercept HTTP response to detect any 400+ HTTP Codes (process() implementation), and if so,
// 2) Force a retry with a delay (HttpRequestRetryHandler.retryRequest() implementation)
final CloseableHttpClient httpClient = HttpClients.custom().addInterceptorLast(new HttpResponseInterceptor() {
    @Override
    public void process(HttpResponse response, HttpContext context) throws HttpException, IOException {
        if (response.getStatusLine().getStatusCode() >= 400) {
            // Throw an IOException to force a retry via HttpRequestRetryHandler.retryRequest()
            throw new IOException("Invalid code returned: " + response.getStatusLine().getStatusCode());
        }       
    }
})
.setRetryHandler(new HttpRequestRetryHandler() {
    @Override
    public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
        if (executionCount > MAX_RETRIES) { // MAX_RETRIES = 3
            return false;
        } else {
            try {
                // Sleep before retrying
                Thread.sleep(DELAY); // DELAY = 1000 MS
            } catch (InterruptedException ex) {
                // ... Log or silently swallow 
            }
            return true;
        }       
    }
})
.build();

final HttpGet getOp = new HttpGet("http://yoururl.com/api/123/");

try {
    return httpClient.execute(getOp, new ResponseHandler<String>() {
        @Override
        public String handleResponse(final HttpResponse response) throws ClientProtocolException, IOException {
            // ... Process response after preliminary HTTP code verification
        }
    });
} catch (IOException ioe) {
    // NOTE: Comes here if all retries have failed, throw error back to caller
    log.error("All retries have been exhausted");
    throw ioe;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接