web-dev-qa-db-ja.com

ApacheHttpクライアントは「[読み取り] I / Oエラー:読み取りがタイムアウトしました」を出力します

Apache httpクライアントv4.5を使用しており、それをRESTクライアントとして使用しています。[読み取り] I/Oエラー:読み取りがタイムアウトしました」というエラーが発生する場合があります。受信したコンテンツを読み取り、それを最後のメッセージとして表示するときのhttpclientフレームワーク。

影響はないようですが、どこから来たのか、どうやって解決できるのか、誰か知っているのではないでしょうか。次のスレッド( link )によると、「マルチスレッド」構成に問題があるようです。

ただし、私はhttpクライアントのデフォルト構成のみを使用しており、バージョンv4を使用している間、「マルチスレッド」を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();
10
megloff

私はhttpclient4.5.2を使用していますが、私の場合、リクエストにタイムアウトを設定すると次のようになりました。

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

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

postRequest.setConfig(requestConfig);
4
Ivan Hristov

このようなエラーが発生する理由の1つは、サーバーが応答時にHTTP接続を閉じるというポリシーに従っているためである可能性があります。その場合は、クライアントによる接続の再利用を無効にすることでこのエラーを回避できるため、クライアントが接続がまだ有効であることを確認する必要はありません。

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

その理由は「マルチスレッド」ではなく「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);
}
}

ソケットタイムアウトを1ミリ秒に設定し、入力ストリームを読み取り、次の結果を返します。

  1. -1つの新しい接続を再作成します
  2. "> 0"再利用接続
  3. タイムアウト例外、接続の再利用
  4. IO例外は新しい接続を再作成します

このメッセージを回避するには、validateAfterInactivityに適切な値を設定します。これは、クライアントソケットのタイムアウト、キープアライブ時間(ConnectionKeepAliveStrategyによって返される)、サーバー接続のタイムアウト、およびidleConnectionMonitorの遅延時間の最小値です。これは接続状態をチェックしません。接続がリセットされると、新しい接続が作成されます。

1
avatas