Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I am using apache http client v4.5 and using it as a REST client. In some cases I recognize an error "[read] I/O error: Read timed out" which comes from the httpclient framework when it reads the received content and shows it as a last message.

It seems not to have an impact, however I wonder if somebody has an idea where it is coming from and how it can be solved. According the following thread (link) it seems to be an issue with the "mutlithreaded" configuration.

However I use only the default configuration of http client and while I am using the version v4 I have no clue how I can set "multithreaded" to false to see if it makes any difference.

I also tried to set the timeouts but it did not helped.

Any hints?

Log:

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"

my Http client initialization

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();
like image 806
megloff Avatar asked Dec 29 '15 15:12

megloff


2 Answers

One of the reasons for having such an error may be that the server follows the policy of closing HTTP connections upon having responded. If that is the case, then this error may be avoided by disabling connection reuse by the client, so there would be no need for the client to verify that the connection is still alive:

httpBuilder.setConnectionReuseStrategy( (response, context) -> false );
like image 122
Sergey Ushakov Avatar answered Nov 05 '22 11:11

Sergey Ushakov


The reason is not "multithread", but "http client Pool".

It is not a error, just test the socket stale, and it is good when you want to reuse a socket. See 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);
}
}

Set socket timeout to 1 ms, and read input stream, return these results:

  1. -1 recreate new connection
  2. ">0" reuse connection
  3. timeout exception, reuse connection
  4. IO exception recreate new connection

To avoid this message, just set proper value for validateAfterInactivity , it's the min value of client socket timeout, keep-alive time(return by ConnectionKeepAliveStrategy), server connection timeout, and the delay time for idleConnectionMonitor. This does not check connection state, if connect is reset, a new connection will be created.

like image 44
avatas Avatar answered Nov 05 '22 09:11

avatas