RestTemplate 调用 https的域名时:出现 nested exception is org.apache.http.NoHttpResponseException: ****:443 failed to respond 错误
现象:
org.springframework.web.client.ResourceAccessException: I/O error on POST request for "https://***.com/*": ***:443 failed to respond; nested exception is org.apache.http.NoHttpResponseException: ***:443 failed to respond
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:673)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:620)
at org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:387)
at com.zkh360.sim.service.kh.impl.KhServiceImpl.requestTransportationTime(KhServiceImpl.java:345)
at com.zkh360.sim.service.date.impl.EstimateDeliveryTimeServiceImpl.acquireTransTime(EstimateDeliveryTimeServiceImpl.java:1176)
at com.zkh360.sim.service.date.impl.EstimateDeliveryTimeServiceImpl.calculateEstimateDeliveryDate(EstimateDeliveryTimeServiceImpl.java:1134)
at com.zkh360.sim.api.DateRelatedApi.lambda$findEstimateDeliveryDate$1(DateRelatedApi.java:135)
at org.springframework.cloud.sleuth.instrument.async.SpanContinuingTraceCallable.call(SpanContinuingTraceCallable.java:54)
at org.springframework.web.context.request.async.WebAsyncManager$5.run(WebAsyncManager.java:327)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.lang.Thread.run(Thread.java:748)
原因分析:
RestTemplate 自带链接池,下次使用时,连接池已经断开了;所以会出现上面的 错误。
解决办法:
RestTemplate 里面使用 短连接。
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import java.io.IOException;
/**
* 短连接
* @author cloudyue
*/
@Slf4j
public class ClientHttpRequestConnectionCloseInterceptor implements ClientHttpRequestInterceptor {
private static final String NO_CONNECTION_UPDATE = "NoConnectionUpdate";
private static final String CONNECTION_UP = "Connection";
private static final String CONNECTION_LOW = "connection";
@Override
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
try {
HttpHeaders headers = httpRequest.getHeaders();
if (headers.containsKey(NO_CONNECTION_UPDATE)) {
log.info("skip connection intercept");
} else {
if (!headers.containsKey(CONNECTION_UP) && !headers.containsKey(CONNECTION_LOW)) {
headers.set(CONNECTION_LOW, "close");
}
}
} catch (Exception e) {
log.error("http request intercept fail, {}", e.getMessage(), e);
}
return clientHttpRequestExecution.execute(httpRequest, bytes);
}
}
RestTemplute配置:
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import javax.net.ssl.SSLContext;
/**
* @desc: RestTemplateConfig.java
* @author: cloud.yue
*/
@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new HookedRestTemplate(generateHttpsRequestFactory())
restTemplate.getInterceptors()
// 默认使用短连接
.add(new ClientHttpRequestConnectionCloseInterceptor());
return restTemplate;
}
public HttpComponentsClientHttpRequestFactory generateHttpsRequestFactory() {
try {
TrustStrategy acceptingTrustStrategy = (x509Certificates, authType) -> true;
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
SSLConnectionSocketFactory connectionSocketFactory = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
HttpClientBuilder httpClientBuilder = HttpClients.custom();
httpClientBuilder.setSSLSocketFactory(connectionSocketFactory);
CloseableHttpClient httpClient = httpClientBuilder.build();
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setHttpClient(httpClient);
factory.setConnectTimeout(60 * 1000);
factory.setReadTimeout(60 * 1000);
return factory;
} catch (Exception e) {
throw new RuntimeException("创建HttpsRestTemplate失败", e);
}
}
}