com.inspect
inspect-base-core
diff --git a/inspect-main/inspect-main-agg/src/main/java/com/inspect/agg/config/AggProperties.java b/inspect-main/inspect-main-agg/src/main/java/com/inspect/agg/config/AggProperties.java
index 7575605..37f4fc3 100644
--- a/inspect-main/inspect-main-agg/src/main/java/com/inspect/agg/config/AggProperties.java
+++ b/inspect-main/inspect-main-agg/src/main/java/com/inspect/agg/config/AggProperties.java
@@ -93,6 +93,14 @@ public class AggProperties {
public static class Http {
private int connectTimeoutMs = 10_000;
private int readTimeoutMs = 30_000;
+ /**
+ * 连接池总连接上限。
+ */
+ private int poolMaxTotal = 100;
+ /** 同一站端的连接上限;一次九画面并发起流按 3 路一批,留足余量。 */
+ private int poolMaxPerRoute = 40;
+ /** 从连接池取空闲连接的等待上限(毫秒),避免池满时无限等待。 */
+ private int connectionRequestTimeoutMs = 3_000;
}
@Data
diff --git a/inspect-main/inspect-main-agg/src/main/java/com/inspect/agg/config/RestTemplateConfig.java b/inspect-main/inspect-main-agg/src/main/java/com/inspect/agg/config/RestTemplateConfig.java
index 6686efe..8113a3a 100644
--- a/inspect-main/inspect-main-agg/src/main/java/com/inspect/agg/config/RestTemplateConfig.java
+++ b/inspect-main/inspect-main-agg/src/main/java/com/inspect/agg/config/RestTemplateConfig.java
@@ -1,19 +1,27 @@
package com.inspect.agg.config;
import java.nio.charset.StandardCharsets;
-import java.time.Duration;
import java.util.List;
+import java.util.concurrent.TimeUnit;
+import org.apache.http.client.config.RequestConfig;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
/**
- * HTTP 客户端配置:RestTemplate + 超时 + UTF-8(站点接口返回中文,必须 UTF-8 解码)。
+ * HTTP 客户端配置:连接池 + 超时 + UTF-8(站点接口返回中文,必须 UTF-8 解码)。
+ *
+ * 聚合层向站端发起的起流/停流请求在打开多画面时会同时出现十几个,串行新建 TCP
+ * 连接会明显放大延迟并让部分请求打满页面 8 秒超时;这里改为复用连接的连接池。
*/
@Configuration
public class RestTemplateConfig {
@@ -27,10 +35,41 @@ public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
+ AggProperties.Http http = aggProperties.getHttp();
+
+ // 连接池:总连接数与单站端连接数都要覆盖“同时在飞的站端请求”。
+ PoolingHttpClientConnectionManager connectionManager =
+ new PoolingHttpClientConnectionManager();
+ connectionManager.setMaxTotal(Math.max(1, http.getPoolMaxTotal()));
+ connectionManager.setDefaultMaxPerRoute(Math.max(1, http.getPoolMaxPerRoute()));
+
+ // 超时与池等待:取连接不能无限等,否则池满时请求会一直挂到页面超时。
+ RequestConfig requestConfig = RequestConfig.custom()
+ .setConnectTimeout(http.getConnectTimeoutMs())
+ .setSocketTimeout(http.getReadTimeoutMs())
+ .setConnectionRequestTimeout(http.getConnectionRequestTimeoutMs())
+ .build();
+
+ CloseableHttpClient httpClient = HttpClients.custom()
+ .setConnectionManager(connectionManager)
+ .setDefaultRequestConfig(requestConfig)
+ // 定期清理失效空闲连接,避免站端重启后一直复用坏连接。
+ .evictExpiredConnections()
+ .evictIdleConnections(30L, TimeUnit.SECONDS)
+ .build();
+
+ // 只在这里指定请求工厂;连接与读取超时统一由上面的 RequestConfig 控制,
+ // 避免与 RestTemplateBuilder 的超时设置叠加产生歧义。
RestTemplate restTemplate = builder
- .setConnectTimeout(Duration.ofMillis(aggProperties.getHttp().getConnectTimeoutMs()))
- .setReadTimeout(Duration.ofMillis(aggProperties.getHttp().getReadTimeoutMs()))
+ .requestFactory(() -> {
+ HttpComponentsClientHttpRequestFactory factory =
+ new HttpComponentsClientHttpRequestFactory(httpClient);
+ factory.setConnectTimeout(http.getConnectTimeoutMs());
+ factory.setReadTimeout(http.getReadTimeoutMs());
+ return factory;
+ })
.build();
+
// 默认 StringHttpMessageConverter 用 ISO-8859-1,会破坏站点返回的中文
List> converters = restTemplate.getMessageConverters();
for (HttpMessageConverter> converter : converters) {
@@ -41,4 +80,4 @@ public class RestTemplateConfig {
}
return restTemplate;
}
-}
\ No newline at end of file
+}
diff --git a/inspect-main/inspect-main-agg/src/main/resources/application.yml b/inspect-main/inspect-main-agg/src/main/resources/application.yml
index 934f631..898a62c 100644
--- a/inspect-main/inspect-main-agg/src/main/resources/application.yml
+++ b/inspect-main/inspect-main-agg/src/main/resources/application.yml
@@ -41,3 +41,16 @@ agg:
# 是否在 /agg/proxy 通用代理的 JSON 响应中递归改写图片/文件地址
rewrite-proxy-response: true
+ # 访问站端接口的 HTTP 客户端;打开多画面时会同时发起多路起流/停流请求。
+ http:
+ # 建立连接超时(毫秒)
+ connect-timeout-ms: ${AGG_HTTP_CONNECT_TIMEOUT_MS:10000}
+ # 读取响应超时(毫秒),需大于站端起流最慢耗时
+ read-timeout-ms: ${AGG_HTTP_READ_TIMEOUT_MS:30000}
+ # 连接池总连接上限
+ pool-max-total: ${AGG_HTTP_POOL_MAX_TOTAL:100}
+ # 同一站端的连接上限
+ pool-max-per-route: ${AGG_HTTP_POOL_MAX_PER_ROUTE:40}
+ # 从池中取空闲连接的等待上限(毫秒)
+ connection-request-timeout-ms: ${AGG_HTTP_CONNECTION_REQUEST_TIMEOUT_MS:3000}
+
diff --git a/pom.xml b/pom.xml
index 4575d65..5f73bbb 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,6 +11,8 @@
3.4.0
+ UTF-8
+ UTF-8
8
8
1.8