소스 검색

fix 修复 gateway sse 连接占用连接池导致响应拒绝问题

疯狂的狮子Li 3 주 전
부모
커밋
11b2d28c07

+ 5 - 0
ruoyi-gateway-mvc/pom.xml

@@ -65,6 +65,11 @@
             <artifactId>hutool-http</artifactId>
         </dependency>
 
+        <dependency>
+            <groupId>org.apache.httpcomponents.client5</groupId>
+            <artifactId>httpclient5</artifactId>
+        </dependency>
+
         <!-- Sa-Token 权限认证 -->
         <dependency>
             <groupId>cn.dev33</groupId>

+ 55 - 0
ruoyi-gateway-mvc/src/main/java/org/dromara/gateway/config/GatewayHttpClientConfig.java

@@ -0,0 +1,55 @@
+package org.dromara.gateway.config;
+
+import lombok.RequiredArgsConstructor;
+import org.apache.hc.core5.util.TimeValue;
+import org.apache.hc.core5.util.Timeout;
+import org.dromara.gateway.config.properties.GatewayHttpClientProperties;
+import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder;
+import org.springframework.boot.http.client.ClientHttpRequestFactorySettings;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.http.client.ClientHttpRequestFactory;
+
+/**
+ * 网关 HttpClient5 配置
+ *
+ * @author RuoYi
+ */
+@Configuration
+@RequiredArgsConstructor
+public class GatewayHttpClientConfig {
+
+    private final GatewayHttpClientProperties properties;
+
+    /**
+     * Gateway MVC 使用 RestClient 转发请求,此处提供 HttpClient5 请求工厂以调整连接池参数
+     */
+    @Bean
+    @RefreshScope
+    public ClientHttpRequestFactory gatewayClientHttpRequestFactory(ClientHttpRequestFactorySettings settings) {
+        return ClientHttpRequestFactoryBuilder.httpComponents()
+            .withConnectionManagerCustomizer(connectionManager -> {
+                connectionManager.setMaxConnTotal(properties.getMaxConnTotal());
+                connectionManager.setMaxConnPerRoute(properties.getMaxConnPerRoute());
+            })
+            .withConnectionConfigCustomizer(connectionConfig -> {
+                if (properties.getConnectionTimeToLive() != null) {
+                    connectionConfig.setTimeToLive(TimeValue.of(properties.getConnectionTimeToLive()));
+                }
+                if (properties.getValidateAfterInactivity() != null) {
+                    connectionConfig.setValidateAfterInactivity(TimeValue.of(properties.getValidateAfterInactivity()));
+                }
+            })
+            .withDefaultRequestConfigCustomizer(requestConfig -> {
+                if (properties.getConnectionRequestTimeout() != null) {
+                    requestConfig.setConnectionRequestTimeout(Timeout.of(properties.getConnectionRequestTimeout()));
+                }
+                if (properties.getResponseTimeout() != null) {
+                    requestConfig.setResponseTimeout(Timeout.of(properties.getResponseTimeout()));
+                }
+            })
+            .build(settings);
+    }
+
+}

+ 51 - 0
ruoyi-gateway-mvc/src/main/java/org/dromara/gateway/config/properties/GatewayHttpClientProperties.java

@@ -0,0 +1,51 @@
+package org.dromara.gateway.config.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.context.annotation.Configuration;
+
+import java.time.Duration;
+
+/**
+ * 网关 HttpClient5 配置
+ *
+ * @author RuoYi
+ */
+@Data
+@Configuration
+@RefreshScope
+@ConfigurationProperties(prefix = "spring.cloud.gateway.httpclient")
+public class GatewayHttpClientProperties {
+
+    /**
+     * 连接池最大连接数
+     */
+    private Integer maxConnTotal = 1000;
+
+    /**
+     * 单个路由最大连接数
+     */
+    private Integer maxConnPerRoute = 500;
+
+    /**
+     * 从连接池获取连接的等待超时时间
+     */
+    private Duration connectionRequestTimeout = Duration.ofSeconds(3);
+
+    /**
+     * 等待下游响应数据的超时时间,SSE 场景通常不设置
+     */
+    private Duration responseTimeout;
+
+    /**
+     * 连接最大存活时间
+     */
+    private Duration connectionTimeToLive;
+
+    /**
+     * 空闲连接可用性校验间隔
+     */
+    private Duration validateAfterInactivity = Duration.ofSeconds(30);
+
+}

+ 14 - 0
script/config/nacos/ruoyi-gateway-mvc.yml

@@ -21,6 +21,20 @@ spring:
     gateway:
       # 打印请求日志(自定义)
       requestLog: true
+      # Gateway MVC 转发使用的 HttpClient5 连接池配置
+      httpclient:
+        # 总最大连接数
+        max-conn-total: 1000
+        # 单个下游路由最大连接数,SSE 长连接较多时需按并发量调整
+        max-conn-per-route: 500
+        # 连接池满时等待可用连接的时间
+        connection-request-timeout: 3s
+        # SSE 场景通常不设置响应超时,避免长连接被网关主动断开
+        # response-timeout: 60s
+        # 连接最大存活时间,不配置则使用 HttpClient5 默认值
+        # connection-time-to-live: 10m
+        # 空闲连接可用性校验间隔
+        validate-after-inactivity: 30s
       server:
         webmvc:
           discovery:

+ 15 - 0
script/config/nacos/ruoyi-gateway.yml

@@ -24,6 +24,21 @@ spring:
       requestLog: true
       server:
         webflux:
+          # Gateway WebFlux 转发使用的 Reactor Netty 连接池配置
+          httpclient:
+            pool:
+              # 使用固定连接池,避免 SSE 长连接占满默认连接池后快速拒绝普通请求
+              type: fixed
+              # 总最大连接数
+              max-connections: 1000
+              # 连接池满时等待可用连接的时间,单位毫秒
+              acquire-timeout: 3000
+              # 空闲连接最大保留时间
+              max-idle-time: 30s
+              # 后台空闲连接回收间隔
+              eviction-interval: 30s
+            # SSE 场景通常不设置响应超时,避免长连接被网关主动断开
+            # response-timeout: 60s
           discovery:
             locator:
               lowerCaseServiceId: true