Apache Httpclient中止请求导致错误

时间:2015-06-09 21:46:01

标签: java apache-httpclient-4.x

我有一个需要解决几个问题的程序。对于每个问题,它可以并行尝试几种不同的算法。只要一个算法解决了问题,我希望其他算法立即终止。某些算法是通过HTTP请求访问的:您将问题发送到服务器,阻止,并且服务器返回答案。如果另一个线程在服务器返回之前找到答案,我希望该线程取消http请求。

我最初使用Spring的RestTemplate,但我无法找到此功能。最终我偶然发现了Apache的httpclient(我使用的是org.apache.httpcomponents:httpclient:4.4.1)。 Apache Httpclient说它可以abort requests,所以我用它如下:

    // fields
        private final Lock lock;
        private final AtomicBoolean activeSolve;
        private HttpPost post;
        private final CloseableHttpClient httpClient = HttpClients.createDefault();


public String makePost() {
    post = new HttpPost("some url");
    final StringEntity stringEntity = new StringEntity("some json string", ContentType.APPLICATION_JSON);
    post.setEntity(stringEntity);
    try {
        lock.lock();
        activeSolve.set(true);
        lock.unlock();
        try (final CloseableHttpResponse httpResponse = httpClient.execute(post)) {
            final String response = EntityUtils.toString(httpResponse.getEntity());
            return response;
        }
    } catch (IOException e) {
        if (post.isAborted()) {
            log.trace("Web request was aborted");
            return "aborted";
        } else {
            throw new RuntimeException("Could not contact server", e);
        }
    } finally {
        lock.lock();
        activeSolve.set(false);
        lock.unlock();
    }
}

我的代码可以从另一个线程中断

   public void interrupt() {
        lock.lock();
        if (activeSolve.get()) {
            post.abort();
        }
        lock.unlock();
    }

这往往会起作用,但是在大约1000个问题的过程中(我发现它很难重现,但如果我跑得足够长,我终于看到了它),我将得到以下堆栈跟踪:

java.lang.IllegalStateException: Connection is not open
at org.apache.http.util.Asserts.check(Asserts.java:34)
at org.apache.http.impl.BHttpConnectionBase.ensureOpen(BHttpConnectionBase.java:132)
at org.apache.http.impl.DefaultBHttpClientConnection.receiveResponseEntity(DefaultBHttpClientConnection.java:177)
at org.apache.http.impl.conn.CPoolProxy.receiveResponseEntity(CPoolProxy.java:172)
at org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java:274)
at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:124)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:271)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:184)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:88)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:107)
at ... my classes

如果我注释掉中断代码并禁用中断,我就会停止看到这个错误。但据我所知,我正确使用了这个库。

如果有人熟悉这个库,你可以让我知道我是如何错误地使用它或者什么序列的事件可能会触发该错误?否则,如果有另一个库取消您发现可靠的http请求,我就不会绑定到这个apache库。

2 个答案:

答案 0 :(得分:1)

我不确定这是否是问题的路径,但是HttpPost被标记为不是线程安全的(参见javadoc)。这很可能会引发您的问题,并可能会解释您在此处看到的内容。

由于它不是线程安全的,因此很难确切知道它将如何失败,这甚至可能取决于JVM的实现。

答案 1 :(得分:0)

  • 如果你稍微改变一下你的方法,并使用一个线程池产生你的线程,一旦你得到一个响应,你可以关闭该池。
  • 如果我在上面的方法中没有错,我看到HttpClient的实例是共享的..在临界区(方法)中移动它,然后看到导致上述问题的中断消失了。
  • 在中断方法中使用原子变量很棒。