Apache HttpClient临时错误:NoHttpResponseException

时间:2012-05-11 21:19:38

标签: java httpclient

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

它主要发生在我打电话然后让我的应用程序闲置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);

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

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

重要 除了搞清楚为什么我会得到例外之外,我遇到的一个重要问题是为什么重试器不在这里工作?

9 个答案:

答案 0 :(得分:93)

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

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

答案 1 :(得分:15)

接受的答案是正确的,但缺乏解决方案。要避免此错误,您可以为this answer中的HTTP客户端添加setHttpRequestRetryHandler(或apache组件4.4的setRetryHandler)。

答案 2 :(得分:3)

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

此问题已在HttpClient 4.4.1中得到解决。请参阅this JIRArelease notes

答案 3 :(得分:2)

如今,大多数HTTP连接都是considered persistent unless declared otherwise。但是,为了节省服务器资源,连接很少永远保持打开,许多服务器的默认连接超时相当短,例如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();
}

答案 4 :(得分:1)

apache http client 4.5.5上的同样问题 添加默认标题

连接:关闭

解决问题

答案 5 :(得分:1)

尽管公认的答案是正确的,但是恕我直言只是一种解决方法。

要清楚:持久连接可能会过时是完全正常的情况。但是不幸的是,当HTTP客户端库无法正确处理它时,这非常糟糕。

由于Apache HttpClient的这种错误行为已多年未解决,因此我绝对希望切换到可以轻松地从过时的连接问题中恢复的库,例如OkHttp。

为什么?

  1. OkHttp默认情况下会缓冲http连接。
  2. 它可以从http连接陈旧并且由于不等幂而无法重试请求的情况下正常恢复(例如POST)。我不能说Apache HttpClient(提到NoHttpResponseException)。
  3. 支持早期草稿和Beta版的HTTP / 2.0。

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

答案 6 :(得分:1)

解决方案:将ReuseStrategy更改为从不

由于此问题非常复杂,并且有许多不同的因素可能导致失败,所以我很高兴在另一篇文章中找到此解决方案:How to solve org.apache.http.NoHttpResponseException

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

builder.setConnectionReuseStrategy(new NoConnectionReuseStrategy());

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

document.querySelectorAll('.svg path').map( o=> 
        if(!o.hasAttribute('fill')) {
            o.setAttribute('fill', '#eee')
        } 
    );

答案 7 :(得分:0)

如果在分配给您的HttpClient的池管理器上设置了disableContentCompression(),并且目标服务器正在尝试使用gzip压缩,则会发生这种情况。

答案 8 :(得分:0)

我也遇到了同样的问题,我通过添加“ connection:close”作为扩展来解决

步骤1:创建一个新的类 ConnectionCloseExtension

import com.github.tomakehurst.wiremock.common.FileSource;
import com.github.tomakehurst.wiremock.extension.Parameters;
import com.github.tomakehurst.wiremock.extension.ResponseTransformer;
import com.github.tomakehurst.wiremock.http.HttpHeader;
import com.github.tomakehurst.wiremock.http.HttpHeaders;
import com.github.tomakehurst.wiremock.http.Request;
import com.github.tomakehurst.wiremock.http.Response;

public class ConnectionCloseExtension extends ResponseTransformer {
  @Override
  public Response transform(Request request, Response response, FileSource files, Parameters parameters) {
    return Response.Builder
        .like(response)
        .headers(HttpHeaders.copyOf(response.getHeaders())
            .plus(new HttpHeader("Connection", "Close")))
        .build();
  }

  @Override
  public String getName() {
    return "ConnectionCloseExtension";
  }
}

第2步:如下所示在wireMockServer中设置扩展类,

final WireMockServer wireMockServer = new WireMockServer(options()
                .extensions(ConnectionCloseExtension.class)
                .port(httpPort));
相关问题