项目中使用HttpClient post方法调用外部服务。
com.sun.jersey.api.client.ClientHandlerException:
org.apache.http.NoHttpResponseException: XXX.XXX.com:80 failed to respond
at com.sun.jersey.client.apache4.ApacheHttpClient4Handler.handle(ApacheHttpClient4Handler.java:187)
at com.sun.jersey.api.client.Client.handle(Client.java:652)
at com.sun.jersey.api.client.filter.LoggingFilter.handle(LoggingFilter.java:217)
at com.cheche365.cheche.signature.client.ClientSignatureFilter.handle(ClientSignatureFilter.java:39)
at com.sun.jersey.api.client.WebResource.handle(WebResource.java:682)
static {
CLIENT_CONFIG = new DefaultApacheHttpClient4Config();
CLIENT_CONFIG.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
SSLContext sslContext = null;
try {
sslContext = SSLContext.getInstance("TLS");
sslContext.init(new KeyManager[0], new TrustManager[]{new DefaultTrustManager()}, new SecureRandom());
} catch (Exception e) {
e.printStackTrace();
}
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.INSTANCE)
.register("https", new SSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER))
.build();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
connectionManager.setMaxTotal(200);
connectionManager.setDefaultMaxPerRoute(100);
HTTP_CLIENT_BUILDER = HttpClients.custom()
.setConnectionManager(connectionManager)
.setDefaultRequestConfig(
RequestConfig.custom()
.setConnectionRequestTimeout(15 * 1000)
.setConnectTimeout(15 * 1000)
.setSocketTimeout(60 * 1000)
.build())
.setRetryHandler(createHttpRequestRetryHandler())
.evictIdleConnections(15, TimeUnit.SECONDS)
.evictExpiredConnections()
.disableRedirectHandling();
}
private static HttpRequestRetryHandler createHttpRequestRetryHandler() {
return (exception, executionCount, context) -> executionCount <= 3 && exception instanceof NoHttpResponseException;
}
异常信息 : com.sun.jersey.api.client.ClientHandlerException: org.apache.http.client.ClientProtocolException
at com.sun.jersey.client.apache4.ApacheHttpClient4Handler.handle(ApacheHttpClient4Handler.java:187)
at com.sun.jersey.api.client.filter.LoggingFilter.handle(LoggingFilter.java:217)
at com.sun.jersey.api.client.Client.handle(Client.java:652)
at com.sun.jersey.api.client.WebResource.handle(WebResource.java:682)
at com.sun.jersey.api.client.WebResource.access$200(WebResource.java:74)
at com.sun.jersey.api.client.WebResource$Builder.method(WebResource.java:634)
Caused by: org.apache.http.client.ClientProtocolException
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:187)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:72)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56)
at com.sun.jersey.client.apache4.ApacheHttpClient4Handler.handle(ApacheHttpClient4Handler.java:171)
... 77 more
Caused by: org.apache.http.client.NonRepeatableRequestException: Cannot retry request with a non-repeatable request entity
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:108)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
... 80 more
Caused by: org.apache.http.NoHttpResponseException: The target server failed to respond
at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:141)
at org.apache.http.impl.conn.DefaultHttpResponseParser.parseHead(DefaultHttpResponseParser.java:56)
at org.apache.http.impl.io.AbstractMessageParser.parse(AbstractMessageParser.java:259)
at org.apache.http.impl.DefaultBHttpClientConnection.receiveResponseHeader(DefaultBHttpClientConnection.java:163)
at org.apache.http.impl.conn.CPoolProxy.receiveResponseHeader(CPoolProxy.java:157)
at org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse(HttpRequestExecutor.java:273)
at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:125)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:272)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:186)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
... 81 more
问题2.1出现是因为请求超时,没有收到响应,使用http请求重试机制,在出现NoHttpResponseException异常时,直接重试,进而出现2.3异常ClientProtocolException 造成原因是Cannot retry request with a non-repeatable request entity,因此修改使用ApacheHttpClient4Config.PROPERTY_ENABLE_BUFFERING协议即可解决。
创建Client的时候增加配置使用ApacheHttpClient4Config.PROPERTY_ENABLE_BUFFERING协议,详细代码如下:
public static Function<String, Client> createClientFunction(Consumer<Client> customerFilter, Boolean needDefaultFilter, Boolean useBufferingProperty) {
return (host) -> {
CloseableHttpClient closeableHttpClient = HTTP_CLIENT_BUILDER.build();
if (useBufferingProperty){
CLIENT_CONFIG.getProperties().put(ApacheHttpClient4Config.PROPERTY_ENABLE_BUFFERING, Boolean.TRUE);
}
Client client = new Client(new ApacheHttpClient4Handler(closeableHttpClient, new BasicCookieStore(), true), CLIENT_CONFIG);
if (needDefaultFilter) {
client.addFilter(new LoggingFilter(Logger.getLogger(ExternalAPI.class.getName())));
}
if (!Objects.isNull(customerFilter)) {
customerFilter.accept(client);
}
return client;
};
}
使用ApacheHttpClient4Config.PROPERTY_ENABLE_BUFFERING协议可能会出现内存溢出问题,源码里面注释如下:
/**
* If {@code true} then chunk encoding will be disabled and entity will be buffered
* in the client in order to calculate the size of the entity. When property
* is {@code false} then chunk encoding will be enabled. In that case the
* property {@link ClientConfig#PROPERTY_CHUNKED_ENCODING_SIZE} can be
* used to control the size of the chunk.
* <p>
* Note that the behaviour of the http client differs from the default client
* configuration in the way that the chunk encoding is enabled by default and must
* be disabled if needed. When entity buffering is enabled then the whole entity is
* buffered and might cause out of memory errors if the entity is too large.
* <p/>
* <p>
* Property must be of a {@link Boolean} type. Default value is {@code false}.
* </p>
*
*/
public static final String PROPERTY_ENABLE_BUFFERING =
"com.sun.jersey.impl.client.httpclient.enableBuffering";