Apache Http Client打印"[read] I/O error: Read timed out"

11

我正在使用Apache Http Client v4.5,并将其用作REST客户端。在某些情况下,我会遇到一个错误“[read] I/O error: Read timed out”,这是来自httpclient框架的,当它读取接收到的内容并显示为最后一条消息时。

这似乎没有影响,但我想知道是否有人知道它来自哪里以及如何解决。根据以下线程(链接),这似乎是“多线程”配置的问题。

然而,我仅使用http client的默认配置,尽管我使用的是v4版本,但我不知道如何将“multithreaded”设置为false,以查看它是否有任何区别。

我还尝试设置超时时间,但没有帮助。

有任何提示吗?

日志:

15:48:05.984 [main] DEBUG org.apache.http.wire - http-outgoing-8 << "HTTP/1.1 200 OK[\r][\n]"
15:48:05.984 [main] DEBUG org.apache.http.wire - http-outgoing-8 << "Date: Tue, 29 Dec 2015 14:48:03 GMT[\r][\n]"
15:48:05.984 [main] DEBUG org.apache.http.wire - http-outgoing-8 << "Server: Apache/2.4.12 (Win32) OpenSSL/1.0.1l PHP/5.6.8[\r][\n]"
15:48:05.984 [main] DEBUG org.apache.http.wire - http-outgoing-8 << "X-Powered-By: PHP/5.6.8[\r][\n]"
15:48:05.985 [main] DEBUG org.apache.http.wire - http-outgoing-8 << "Cache-Control: nocache, private[\r][\n]"
15:48:05.985 [main] DEBUG org.apache.http.wire - http-outgoing-8 << "Content-Length: 99[\r][\n]"
15:48:05.985 [main] DEBUG org.apache.http.wire - http-outgoing-8 << "Keep-Alive: timeout=5, max=99[\r][\n]"
15:48:05.985 [main] DEBUG org.apache.http.wire - http-outgoing-8 << "Connection: Keep-Alive[\r][\n]"
15:48:05.985 [main] DEBUG org.apache.http.wire - http-outgoing-8 << "Content-Type: application/json[\r][\n]"
15:48:05.985 [main] DEBUG org.apache.http.wire - http-outgoing-8 << "[\r][\n]"
15:48:05.985 [main] DEBUG org.apache.http.wire - http-outgoing-8 << "{"success":true,"data":{"id":1946,"location":"http:\/\/localhost:9001\/shop\/api\/articles\/1946"}}"
15:48:06.016 [main] DEBUG org.apache.http.wire - http-outgoing-8 << "[read] I/O error: Read timed out"

我的 Http 客户端初始化

HttpClientBuilder httpBuilder = HttpClientBuilder.create();

//      set timeout did not helped
//      RequestConfig.Builder requestBuilder = RequestConfig.custom();
//      requestBuilder = requestBuilder.setConnectTimeout(timeout);
//      requestBuilder = requestBuilder.setConnectionRequestTimeout(timeout);
//      requestBuilder = requestBuilder.setSocketTimeout(timeout);
//      httpBuilder.setDefaultRequestConfig(requestBuilder.build());

HttpClient httpClient = httpBuilder.build();

它不会打印任何东西。你的代码 打印了它。不清楚你在问什么。 - user207421
你使用的是哪个版本的Apache HTTP客户端?我想问一下,是否有人知道为什么会出现“[read] I/O error: Read timed out”错误消息。 - megloff
我也看到了这个问题。值得注意的是,没有抛出任何异常,并且它不被记录为错误,只是作为调试信息。 - BrianC
这似乎与使用HttpClient的多线程连接池有关,但实际上并不会导致问题。 - BrianC
3个回答

5

我正在使用httpclient 4.5.2,设置请求超时时间对我的情况有帮助:

HttpPost postRequest = new HttpPost("https://...");
postRequest.setHeader(..., ...);

RequestConfig requestConfig = RequestConfig.custom()
                        .setSocketTimeout(1000)
                        .setConnectTimeout(1000)
                        .build();

postRequest.setConfig(requestConfig);

4

出现此类错误的原因之一可能是服务器遵循在响应后关闭HTTP连接的策略。如果是这种情况,那么可以通过禁用客户端的连接重用来避免此错误,因此无需客户端验证连接是否仍然存活:

httpBuilder.setConnectionReuseStrategy( (response, context) -> false );

2
你也可以使用新的 org.apache.http.impl.NoConnectionReuseStrategy(),而不是为此创建自己的(尽管微不足道的)lambda函数。 - Spork

4

原因不在于“多线程”,而在于“HTTP客户端池”。

这不是一个错误,只是测试套接字陈旧度,并且当您想要重用套接字时非常好。 请参见org/apache/httpcomponents/httpcore/4.4.10/httpcore-4.4.10-sources.jar!/ org/apache/http/pool/AbstractConnPool.java

for (;;) {
    final E leasedEntry = getPoolEntryBlocking(route, state, timeout, tunit, this);
    if (validateAfterInactivity > 0)  {
        if (leasedEntry.getUpdated() + validateAfterInactivity <= System.currentTimeMillis()) {
            if (!validate(leasedEntry)) {
                leasedEntry.close();
                release(leasedEntry, false);
                continue;
            }
        }
    }
    entryRef.set(leasedEntry);
    done.set(true);
    onLease(leasedEntry);
    if (callback != null) {
        callback.completed(leasedEntry);
    }
    return leasedEntry;
}

protected boolean validate(final CPoolEntry entry) {
    return !entry.getConnection().isStale();
}

public boolean isStale() {
    if (!isOpen()) {
        return true;
    }
    try {
        final int bytesRead = fillInputBuffer(1);
        return bytesRead < 0;
    } catch (final SocketTimeoutException ex) {
        return false;
    } catch (final IOException ex) {
        return true;
    }
}

org/apache/httpcomponents/httpcore/4.4.10/httpcore-4.4.10-sources.jar!/org/apache/http/impl/BHttpConnectionBase.java

private int fillInputBuffer(final int timeout) throws IOException {
final Socket socket = this.socketHolder.get();
final int oldtimeout = socket.getSoTimeout();
try {
    socket.setSoTimeout(timeout);
    return this.inbuffer.fillBuffer();
} finally {
    socket.setSoTimeout(oldtimeout);
}
}

将socket超时时间设置为1毫秒,并读取输入流,返回以下结果:

  1. -1 重建新连接
  2. ">0" 重用连接
  3. 超时异常,重用连接
  4. IO异常 重新建立连接

为避免出现此消息,请设置适当的validateAfterInactivity值,它是客户端socket超时时间、保持活动时间(由ConnectionKeepAliveStrategy返回)、服务器连接超时时间和idleConnectionMonitor的延迟时间的最小值。但这不会检查连接状态,如果连接被重置,则会创建一个新连接。


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