解决用apache CloseableHttpClient发送请求出现偶发性网络访问异常: java.net.SocketException: Connection reset
背景:
在springboot 项目里使用CloseableHttpClient给别的服务端发送请求,服务端返回网络访问异常: java.net.SocketException: Connection reset
调查:
通过调查日志发现
21号 9:34 失败 9:56 成功
22号 9:00 失败 10:21 成功 10:22 成功
23号 9:00 失败
24号 9:00 成功 14:16 失败 14:17 两次成功 16:47 失败 16:52 成功
25号 9:00 失败
26号 9:00 成功
27号 9:00 失败
且服务端抓包反馈网口接口未进行https的三次握手,因此向域名服务器的请求被拒绝
分析:基本是一次成功一次失败,成功后再失败 之间间隔了好几个小时,成功后短时间内还是成功,所以推测可能是服务端那边设置了超时自动关闭连接,且未发送FIN通知我们系统, 所以我们系统连接池复用了上次那个无效的连接,导致没有进行https的三次握手。
服务端关闭连接没有发FIN通知我们的原因可能是设置的无状态防火墙

解决办法:
修复我们系统连接池的配置,我们系统使用apache的CloseableHttpClient,且使用的都是默认配置,
打印之后发现超时时间都是-1,需要自己重新设置超时时间,且需加上定期验证功能,参考下面的代码:

@Bean
//绕过ssl证书验证
public static CloseableHttpClient wrapClient() {
try {
// 创建SSL上下文
SSLContext sslContext = SSLContext.getInstance("TLS");
X509TrustManager trustManager = new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {}
public void checkServerTrusted(X509Certificate[] certs, String authType) {}
};
sslContext.init(null, new TrustManager[]{trustManager}, null);
// 创建socket工厂注册表,同时支持HTTP和HTTPS
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE))
.build();
// 使用注册表创建连接管理器
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
connectionManager.setMaxTotal(50);
connectionManager.setDefaultMaxPerRoute(10);
connectionManager.setValidateAfterInactivity(30000);// 空闲连接验证时间30秒
// 配置请求参数
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(30000)
.setSocketTimeout(30000)
.setConnectionRequestTimeout(2000)
.build();
HttpRequestRetryHandler retryHandler = new DefaultHttpRequestRetryHandler(2, true);
// 打印连接池配置日志
log.info("HttpClient连接池配置:");
log.info(" - 最大总连接数(MaxTotal): {}", connectionManager.getMaxTotal());
log.info(" - 每路由最大连接数(DefaultMaxPerRoute): {}", connectionManager.getDefaultMaxPerRoute());
// 打印超时配置日志
log.info("HttpClient超时配置:");
log.info(" - 连接超时(ConnectTimeout): {}ms", requestConfig.getConnectTimeout());
log.info(" - Socket读取超时(SocketTimeout): {}ms", requestConfig.getSocketTimeout());
log.info(" - 连接池获取连接超时(ConnectionRequestTimeout): {}ms", requestConfig.getConnectionRequestTimeout());
// 打印重试配置日志
log.info("HttpClient重试配置:");
log.info(" - 最大重试次数: 2");
// 构建客户端时不再单独设置SSLSocketFactory
return HttpClients.custom()
.setConnectionManager(connectionManager)
.setDefaultRequestConfig(requestConfig)
.setRetryHandler(retryHandler)
.build();
} catch (Exception e) {
log.error("创建HttpClient失败", e);
return HttpClients.createDefault();
}
}
备注:
1、主要配置是connectionManager.setValidateAfterInactivity(30000),
设置空闲连接验证时间30秒,即当连接空闲时间超过此值后,在下次使用前会自动触发有效性验证(如发送测试请求、检查TCP状态),确保连接可用。
如果验证失败,会进行下面操作:
从空闲池移除:连接管理器将该连接从空闲池中删除。
创建新连接:根据连接池的配置(如 maxTotal),尝试新建一个有效连接。
重试机制:如果配置了 HttpRequestRetryHandler,可能对当前请求进行重试(如重试 3 次)。
2、然后就是连接超时配置,其他的可自行参考需不需要。
更多推荐


所有评论(0)