在HttpClient请求之间长时间休眠时连接已关闭

时间:2015-04-18 05:44:56

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

我第一次使用Apache HttpComponents,特别是HttpClient。我有一个相当基本的用例。我每N秒轮询一些第三方服务器(在这种情况下,他们的API期待POST)。

CloseableHttpClient httpclient = HttpClients.custom().
    setDefaultCookieStore(cookieStore).build();

然后我在循环中执行一些POST请求...使用:

while (!someCondition) {
    HttpPost httpPost = ...
    httpclient.execute(httpPost)
    Thread.sleep(SOME_TIME)
}

我注意到如果我睡了很长时间,比如3分钟,那么我就不会从服务器那里得到回复,并且连接每次都会一直死掉:

DEBUG [org.apache.http.wire] http-outgoing-1 << "end of stream"
DEBUG [org.apache.http.impl.conn.DefaultManagedHttpClientConnection] http-outgoing-1: Close connection
DEBUG [org.apache.http.impl.conn.DefaultManagedHttpClientConnection] http-outgoing-1: Shutdown connection
DEBUG [org.apache.http.impl.execchain.MainClientExec] Connection discarded
DEBUG [org.apache.http.impl.conn.DefaultManagedHttpClientConnection] http-outgoing-1: Close connection

我不相信它是服务器。我可能没有正确使用HttpComponents,或者我的配置错误。如果我将它设置为较短的持续时间,例如1分钟,则可以正常工作(我确实注意到它在运行约15分钟后死亡 - 因此每隔十五分钟一次)。

要发送请求,我将它包装在一些Java 8 lambdas中并利用资源尝试,但我不认为这很重要:

private <R> R sendRequest(HttpUriRequest request, Function<String, R> func) {
    try {
        try (CloseableHttpResponse resp = this.httpclient.execute(request)) {
            HttpEntity entity = resp.getEntity();

            String responseString = EntityUtils.toString(entity);

            R result = null;
            if (func != null) {
                result = func.apply(responseString);
            }

            EntityUtils.consume(entity);
            return result;
        }
    } catch (IOException e) {
        e.printStackTrace();
        throw new RuntimeException("Error sending request: " + request, e);
    }
}

实际例外是:

Caused by: org.apache.http.NoHttpResponseException: myserver.com:80 failed to respond
    at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:143)
    at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:57)
    at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:261)
    at org.apache.http.impl.DefaultBHttpClientConnection.receiveResponseHeader(DefaultBHttpClientConnection.java:165)
    at org.apache.http.impl.conn.CPoolProxy.receiveResponseHeader(CPoolProxy.java:167)
    at org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java:272)
    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)

2 个答案:

答案 0 :(得分:1)

HTTP服务器非常普遍且自然地关闭在最长不活动时段内空闲的持久连接,以节省资源。在您的特定情况下,如果客户端每30秒生成一个请求,则服务器保持连接活动,而如果连接保持空闲时间更长,它最终会在服务器端关闭(并且变为半封闭或过时的&#39;在客户端)。下次客户端从池中租用连接并尝试使用它来执行请求时,执行将失败并出现I / O异常。这是完全正常的,可以预期。 HttpClient尝试通过执行所谓的陈旧连接检查来缓解问题,以便找出连接是否已被相对端点关闭或者否则变为无效。过时&#39;陈旧&#39;支票相对昂贵,应谨慎使用。从版本4.4开始,HttpClient通过有选择地执行检查来寻求提高开箱即用的性能。版本4.4中存在缺陷,它基本上完全禁用了连接验证。请升级到4.4.1版本,问题应该消失。

尽管如此,我仍然认为在您的情况下,最佳行动方案是在长时间不活动之前使用HttpClientConnectionManager#closeIdleConnections

逐出连接池中的所有持久连接

答案 1 :(得分:0)

我遇到了类似的问题(NoHttpResponseException:myserver:port无法响应),所有GET请求都在工作,所有POST和PUT请求都失败了。这是由于HTTP客户端默认压缩请求主体和服务器不期望它压缩并将请求视为空主体。

这些是服务器端的日志:

[2017-03-01 11:32:51,074][WARN ][http.netty][host-elasticsearch] Caught exception while handling client http traffic, closing connection [details]
TransportException[Support for compressed content is disabled. You can enable it with http.compression=true]

[2017-03-01 11:32:51,074][WARN ][http.netty] [host-elasticsearch] Caught exception while handling client http traffic, closing connection [details]
java.lang.IllegalStateException: received HttpChunk without HttpMessage

我正在使用jerseyClient(Dropwizard)并解决了在yaml配置中设置的问题:

jersey-client:
  gzipEnabled: false

希望这有助于某人。