【问题标题】:Apache Http Client prints "[read] I/O error: Read timed out""Apache Http 客户端打印“[读取] I/O 错误:读取超时”
【发布时间】:2016-04-03 12:02:00
【问题描述】:

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

它似乎没有影响,但我想知道是否有人知道它来自哪里以及如何解决它。根据以下线程 (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();

【问题讨论】:

  • 它不打印任何东西。 您的代码 打印它。不清楚你在问什么,
  • 您使用的是哪个版本的 apache http 客户端?好吧,我在问是否有人知道为什么会出现错误消息“[read] I/O error: Read timed out”。
  • 我也看到了。还值得注意的是,没有抛出异常,也没有记录为错误,就像调试一样。
  • 这似乎与多线程使用 HttpClient 的连接池有关,但实际上并不会导致问题。

标签: java multithreading timeout httpclient apache-httpcomponents


【解决方案1】:

我使用的是 httpclient 4.5.2,就我而言,设置请求的超时时间会有所帮助:

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

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

postRequest.setConfig(requestConfig);

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • 你也可以使用新的 org.apache.http.impl.NoConnectionReuseStrategy() 而不是为它创建你自己的(尽管微不足道的)lambda
    【解决方案3】:

    原因不是“多线程”,而是“http client Pool”。

    这不是错误,只是测试socket stale,当你想重用一个socket时很好。 看 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 timeout为1 ms,并读取输入流,返回这些结果:

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

    为避免此消息,只需为 validateAfterInactivity 设置适当的值,它是 客户端套接字超时、保活时间(由 ConnectionKeepAliveStrategy 返回)、服务器连接超时、idleConnectionMonitor 的延迟时间的最小值。 这不会检查连接状态,如果重置连接,将创建一个新连接。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-08-16
      • 2021-09-11
      • 2015-09-15
      • 2021-08-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多