Apache HttpClient临时错误:NoHttpResponseException

Em *_* Ae 66 java httpclient

我有一个web服务,它接受一个带有XML的POST方法.它工作正常,然后在一些随机的场合,它无法与服务器通信抛出IOException与消息The target server failed to respond.后续调用工作正常.

它主要发生在我打电话然后让我的应用程序闲置10-15分钟时.我之后的第一个调用会返回此错误.

我尝试了几件事......

我设置重试处理程序就像

HttpRequestRetryHandler retryHandler = new HttpRequestRetryHandler() {

            public boolean retryRequest(IOException e, int retryCount, HttpContext httpCtx) {
                if (retryCount >= 3){
                    Logger.warn(CALLER, "Maximum tries reached, exception would be thrown to outer block");
                    return false;
                }
                if (e instanceof org.apache.http.NoHttpResponseException){
                    Logger.warn(CALLER, "No response from server on "+retryCount+" call");
                    return true;
                }
                return false;
            }
        };

        httpPost.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, retryHandler);
Run Code Online (Sandbox Code Playgroud)

但这次重试从未被召唤过.(是的,我正在使用right instanceof子句).虽然调试这个类永远不会被调用.

我甚至尝试过设置HttpProtocolParams.setUseExpectContinue(httpClient.getParams(), false);但没有用.有人可以建议我现在能做什么吗?

重要 除了弄清楚为什么我得到例外之外,我所关注的一个重要问题是为什么重试操作员不在这里工作?

ok2*_*k2c 102

最有可能由连接管理器保持活动的持久连接变得陈旧.也就是说,目标服务器在其端部关闭连接,而HttpClient无法对该事件作出反应,同时连接处于空闲状态,从而使连接半关闭或"失效".通常这不是问题.HttpClient采用多种技术来验证从池中租用的连接有效性.即使禁用过时连接检查并且使用过时连接来传输请求消息,请求执行通常在使用SocketException的写操作中失败并自动重试.但是在某些情况下,写操作可以在没有异常的情况下终止,随后的读操作返回-1(流结束).在这种情况下,HttpClient别无选择,只能假设请求成功,但服务器很可能由于服务器端的意外错误而无法响应.

解决这种情况的最简单方法是在一段时间不活动后驱逐已过时的连接和连接,这些连接和连接比池中的1分钟更长.有关详细信息,请参阅HttpClient教程的此部分.

  • 使用此处建议的一分钟超时正是我的案例中的问题原因:apache 客户端与本地 jetty 服务器通信。AFAIK 默认情况下,jetty 在 30 秒不活动后关闭连接。设置“connectionManager.setValidateAfterInactivity(500)”,即半秒解决了我的问题。我想,几秒钟也同样有效,但我不在乎。 (3认同)
  • 这取决于许多因素。只要服务器在方法安全和幂等方面符合 HTTP 规范,它_应该_对于安全和幂等的方法是安全的。 (2认同)

Jeh*_*lio 17

接受的答案是对的,但缺乏解决方案.要避免此错误,您可以为此HTTP应用程序添加setHttpRequestRetryHandler(或apache组件4.4的setRetryHandler),如此答案中所示.

  • @Jehy根本不是绝对安全... NoHttpResponseException意味着客户端没有得到响应,不是服务器没有得到和/或处理请求 (4认同)
  • 鉴于最初的问题指定了POST,重试处理程序在这里是否正确? (2认同)
  • 您是在问POST是否可以重试吗?作为设计原则,PUT是幂等的。重试POST可能会导致意想不到的后果。再一次,并非所有的PUT都是幂等的。 (2认同)

Bri*_*new 6

HttpClient 4.4在此区域中存在一个错误,该错误与在返回给请求者之前验证可能过时的连接有关。它没有验证连接是否陈旧,然后导致立即中断NoHttpResponseException

此问题已在HttpClient 4.4.1中解决。请参阅此JIRA发行说明

  • 我使用的是4.5.3版本,但仍收到** NoHttpResponseException **。就我而言,我在每个第4个或第5个连续请求中都收到此Exception,而不是在第一个请求中。知道我的情况可能是什么原因吗? (8认同)

G. *_*cki 6

虽然接受的答案是正确的,但恕我直言,这只是一种解决方法。

需要明确的是:持久连接可能会变得陈旧,这是一种完全正常的情况。但不幸的是,当 HTTP 客户端库无法正确处理它时,情况就非常糟糕了。

由于 Apache HttpClient 中的这种错误行为多年来都没有得到修复,所以我肯定更愿意切换到可以轻松从过时连接问题中恢复的库,例如 OkHttp。

为什么?

  1. OkHttp 默认池化 http 连接。
  2. 当http连接变得陈旧并且请求由于不是幂等(例如POST)而无法重试时,它可以优雅地从情况中恢复。我不能谈论 Apache HttpClient(提到过NoHttpResponseException)。
  3. 从早期草案和测试版本开始支持 HTTP/2.0。

当我切换到 OkHttp 时,我的问题就NoHttpResponseException永远消失了。


Ber*_*eux 5

如今,除非另有声明,否则大多数 HTTP 连接都被视为持久连接。然而,为了节省服务器资源,连接很少会永远保持打开状态,许多服务器的默认连接超时时间相当短,例如 Apache httpd 2.2 及更高版本的连接超时时间为 5 秒。

org.apache.http.NoHttpResponseException错误很可能是由服务器关闭的一个持久连接引起的。

可以设置 Apache Http 客户端池中未使用的连接保持打开状态的最长时间(以毫秒为单位)。

使用 Spring Boot,实现此目的的一种方法是:

public class RestTemplateCustomizers {
    static public class MaxConnectionTimeCustomizer implements RestTemplateCustomizer {

        @Override
        public void customize(RestTemplate restTemplate) {
            HttpClient httpClient = HttpClientBuilder
                .create()
                .setConnectionTimeToLive(1000, TimeUnit.MILLISECONDS)
                .build();

            restTemplate.setRequestFactory(
                new HttpComponentsClientHttpRequestFactory(httpClient));
        }
    }
}

// In your service that uses a RestTemplate
public MyRestService(RestTemplateBuilder builder ) {
    restTemplate = builder
         .customizers(new RestTemplateCustomizers.MaxConnectionTimeCustomizer())
         .build();
}
Run Code Online (Sandbox Code Playgroud)


小智 5

解决方案:将 ReuseStrategy 更改为 never

由于这个问题非常复杂,并且有很多不同的因素可能会失败,我很高兴在另一篇文章中找到这个解决方案:如何解决 org.apache.http.NoHttpResponseException

永远不要重用连接:在 org.apache.http.impl.client.AbstractHttpClient 中配置:

httpClient.setReuseStrategy(new NoConnectionReuseStrategy());
Run Code Online (Sandbox Code Playgroud)

可以在 org.apache.http.impl.client.HttpClientBuilder 构建器上配置相同的内容:

builder.setConnectionReuseStrategy(new NoConnectionReuseStrategy());
Run Code Online (Sandbox Code Playgroud)