背景:
在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、然后就是连接超时配置,其他的可自行参考需不需要。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐