我有一个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);
但没有用。有人可以建议我现在能做什么吗?
重要 除了搞清楚为什么我会得到例外之外,我遇到的一个重要问题是为什么重试器不在这里工作?
答案 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 JIRA和release 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。
为什么?
NoHttpResponseException
)。当我切换到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));