如何计算所有发送的HTTP请求,重试?

时间:2014-03-05 10:45:33

标签: apache-httpclient-4.x apache-httpcomponents

某些用例需要能够计算Apache API发送的请求。例如,在大量请求Web API时,哪个API需要通过API密钥进行身份验证,以及哪个TOS限制请求及时计算每个密钥。

对此案例更具体,我正在请求https://domain1/fooNeedNoKey,并根据其响应分析数据,我请求https://domain2/fooNeedKeyWithRequestsCountRestrictions。这些1到2请求序列的所有发送都是通过单个org.apache.http.impl.client.FutureRequestExecutionService执行的。

截至目前,根据org.apache.httpcomponents:httpclient:4.3.3,我正在使用这些API元素:

  • org.apache.http.impl.client.FutureRequestExecutionService,用于执行多线程HTTP请求。它提供时间指标(HTTP线程在终止之前花了多少时间),但没有请求计数器指标
  • final CloseableHttpClient httpClient = HttpClients.custom() // the auto-retry feature of the Apache API will retry up to 5 // times on failure, being also allowed to send again requests // that were already sent if necessary (I don't really understand // the purpose of the second parameter below) .setRetryHandler(new StandardHttpRequestRetryHandler(5, true)) // for HTTP 503 'Service unavailable' errors, also retrying up to // 5 times, waiting 500ms between each retry. Guessed is that those // 5 retries are part of the previous "global" 5 retries setting. // The below setting, when used alone, would allow to only enable // retries for HTTP 503, or to get a greater count of retries for // this specific error .setServiceUnavailableRetryStrategy(new DefaultServiceUnavailableRetryStrategy(5, 500)) .build();,它自定义Apache API重试行为

回到主题:

  • 可以通过扩展在
  • 之前引用的Apache API重试相关类来创建请求计数器
  • 或者,an Apache API support unrelated ticket往往表明此请求计数器指标可用并从API转发到Java NIO

修改1 : 看起来Apache API不允许这样做。 从API内部引用,RetryExec不能在API代码I / O中扩展:

package org.apache.http.impl.execchain;

public class RetryExec implements ClientExecChain {
    ..
    public CloseableHttpResponse execute(
            final HttpRoute route,
            final HttpRequestWrapper request,
            final HttpClientContext context,
            final HttpExecutionAware execAware) throws IOException, HttpException {
        ..
        for (int execCount = 1;; execCount++) {
            try {
                return this.requestExecutor.execute(route, request, context, execAware);
            } catch (final IOException ex) {
                ..
                if (retryHandler.retryRequest(ex, execCount, context)) {
                    ..
        }
        ..
    }
}

'execCount'变量是必需的信息,因为它只在本地使用,所以无法访问。

同样,可以扩展'retryHandler',并手动计算其中的请求,但'retryHandler.retryRequest(ex,execCount,context)'未提供'request'变量,因此无法知道什么我们正在递增一个计数器(可能只想增加发送到特定域的请求的计数器)。

我没有Java的想法。第三方替代方案:让Java进程轮询磁盘上的文件,由计算所需请求的shell脚本管理。当然它会进行大量的磁盘读访问,并且将成为硬件杀手选项。

1 个答案:

答案 0 :(得分:0)

好的,解决方法很简单,API的HttpContext类就是为了这个目的:

// optionnally, in case your HttpCLient is configured for retry
class URIAwareHttpRequestRetryHandler extends StandardHttpRequestRetryHandler {
    public URIAwareHttpRequestRetryHandler(final int retryCount, final boolean requestSentRetryEnabled)
    {
        super(retryCount, requestSentRetryEnabled);
    }

    @Override
    public boolean retryRequest(final IOException exception, final int executionCount, final HttpContext context)
    {
        final boolean ret = super.retryRequest(exception, executionCount, context);
        if (ret) {
            doForEachRequestSentOnURI((String) context.getAttribute("requestURI"));
        }
        return ret;
    }
}

// optionnally, in addition to the previous one, in case your HttpClient has specific settings for the 'Service unavailable' errors retries 
class URIAwareServiceUnavailableRetryStrategy extends DefaultServiceUnavailableRetryStrategy {
    public URIAwareServiceUnavailableRetryStrategy(final int maxRetries, final int retryInterval)
    {
        super(maxRetries, retryInterval);
    }

    @Override
    public boolean retryRequest(final HttpResponse response, final int executionCount, final HttpContext context)
    {
        final boolean ret = super.retryRequest(response, executionCount, context);
        if (ret) {
            doForEachRequestSentOnURI((String) context.getAttribute("requestURI"));
        }
        return ret;
    }
}

// main HTTP querying code: retain the URI in the HttpContext to make it available in the custom retry-handlers code
httpContext.setAttribute("requestURI", httpGET.getURI().toString());
try {
    httpContext.setAttribute("requestURI", httpGET.getURI().toString());
    httpClient.execute(httpGET, getHTTPResponseHandlerLazy(), httpContext);
    // if request got successful with no need of retries, of if it succeeded on the last send: in any cases, this is the last query sent to server and it got successful
    doForEachRequestSentOnURI(httpGET.getURI().toString());
} catch (final ClientProtocolException e) {
    // if request definitively failed after retries: it's the last query sent to server, and it failed
    doForEachRequestSentOnURI(httpGET.getURI().toString());
} catch (final IOException e) {
    // if request definitively failed after retries: it's the last query sent to server, and it failed
    doForEachRequestSentOnURI(httpGET.getURI().toString());
} finally {
    // restoring the context as it was initially
    httpContext.removeAttribute("requestURI");
}

解决。