From 8233ff80408be5ee5c71d765aeebd189069b4b42 Mon Sep 17 00:00:00 2001 From: zhangjf Date: Thu, 19 Feb 2026 21:39:30 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E5=A4=9A=E7=A7=9F?= =?UTF-8?q?=E6=88=B7=E4=B8=93=E5=B1=9E=E5=AE=9E=E4=BE=8B=EF=BC=88=E9=80=97?= =?UTF-8?q?=E5=8F=B7=E5=88=86=E9=9A=94=E7=9A=84=E7=A7=9F=E6=88=B7ID?= =?UTF-8?q?=E5=88=97=E8=A1=A8=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 功能增强: - TENANT_ID 支持逗号分隔的多个租户 ID - 一个实例可以服务多个指定租户 实例类型: 共享实例: TENANT_ID = "" (空) 单租户专属: TENANT_ID = "VIP_001" 多租户专属: TENANT_ID = "VIP_001,VIP_002,VIP_003" 路由逻辑: 1. 解析实例 metadata.tenant-id 为租户列表 2. 检查请求 tenantId 是否在列表中 3. 匹配成功 → 专属实例 4. 匹配失败 → 回退共享实例 使用场景: - 大客户独占实例(单租户) - 多个小客户共享一个实例(多租户) - 普通客户使用公共实例(共享) --- docker-compose.yml | 9 +- .../loadbalancer/TenantAwareLoadBalancer.java | 61 ++++++--- .../TenantAwareLoadBalancerTest.java | 122 ++++++++++++++---- .../TenantAwareLoadBalancer.class | Bin 10891 -> 11998 bytes ...adbalancer.TenantAwareLoadBalancerTest.xml | 45 +++++-- ...adbalancer.TenantAwareLoadBalancerTest.txt | 2 +- .../TenantAwareLoadBalancerTest.class | Bin 5847 -> 6503 bytes logs/fund-sys-shared-8100.log | 18 +++ logs/fund-sys-tenant-vip001-8101.log | 36 ++++++ logs/fund-sys/error.log | 48 +++++++ logs/fund-sys/info.log | 6 + 11 files changed, 286 insertions(+), 61 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 794354e..3880a0b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -185,7 +185,10 @@ services: REDIS_PORT: 6379 JAVA_OPTS: -Xms256m -Xmx512m # 租户 ID - 共享实例(空值,所有租户可用) - TENANT_ID: "" + # TENANT_ID: "" + + # 租户 ID - 多租户专属实例(逗号分隔,服务多个租户) + TENANT_ID: "VIP_001,VIP_002,VIP_003" ports: - "8100:8100" depends_on: @@ -227,8 +230,8 @@ services: REDIS_HOST: redis REDIS_PORT: 6379 JAVA_OPTS: -Xms256m -Xmx512m - # 租户 ID - VIP_001 专属实例 - TENANT_ID: "VIP_001" + # 租户 ID - 单租户专属实例 + TENANT_ID: "VIP_004" ports: - "8101:8101" depends_on: diff --git a/fund-common/src/main/java/com/fundplatform/common/loadbalancer/TenantAwareLoadBalancer.java b/fund-common/src/main/java/com/fundplatform/common/loadbalancer/TenantAwareLoadBalancer.java index 9f530a3..646aabb 100644 --- a/fund-common/src/main/java/com/fundplatform/common/loadbalancer/TenantAwareLoadBalancer.java +++ b/fund-common/src/main/java/com/fundplatform/common/loadbalancer/TenantAwareLoadBalancer.java @@ -14,6 +14,8 @@ import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBal import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; import reactor.core.publisher.Mono; +import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.ThreadLocalRandom; @@ -31,14 +33,15 @@ import java.util.stream.Collectors; * *

路由规则:

*
- * 1. 查找 metadata.tenant-id == 请求.tenantId 的实例 → VIP 专属实例
+ * 1. 查找 metadata.tenant-id 包含请求 tenantId 的实例 → 专属实例
  * 2. 找不到 → 回退到共享实例(metadata.tenant-id 为空)
  * 
* *

实例配置:

*
- * 共享实例: metadata.tenant-id = "" (空)
- * VIP 实例: metadata.tenant-id = "VIP_001"
+ * 共享实例:     metadata.tenant-id = "" (空)
+ * 单租户专属:   metadata.tenant-id = "VIP_001"
+ * 多租户专属:   metadata.tenant-id = "VIP_001,VIP_002,VIP_003"
  * 
*/ public class TenantAwareLoadBalancer implements ReactorServiceInstanceLoadBalancer { @@ -130,9 +133,11 @@ public class TenantAwareLoadBalancer implements ReactorServiceInstanceLoadBalanc * *

路由策略:

*
    - *
  1. 优先选择租户专属实例(metadata.tenant-id 匹配)
  2. + *
  3. 优先选择租户专属实例(metadata.tenant-id 包含请求的 tenantId)
  4. *
  5. 回退到共享实例(metadata.tenant-id 为空或不存在)
  6. *
+ * + *

tenant-id 支持逗号分隔的多个租户 ID

*/ List filterByTenantId(List instances, String tenantId) { if (instances == null || instances.isEmpty()) { @@ -151,14 +156,13 @@ public class TenantAwareLoadBalancer implements ReactorServiceInstanceLoadBalanc if (tenantId != null && !tenantId.isEmpty()) { List tenantInstances = instances.stream() .filter(inst -> { - Map metadata = inst.getMetadata(); - if (metadata == null) return false; - String instanceTenantId = metadata.get("tenant-id"); - boolean match = tenantId.equals(instanceTenantId); - if (match) { - logger.debug("[TenantLB] 匹配租户专属实例:{}:{}", inst.getHost(), inst.getPort()); + List allowedTenants = parseTenantIds(inst); + if (allowedTenants.contains(tenantId)) { + logger.debug("[TenantLB] 匹配租户专属实例:{}:{} (允许租户: {})", + inst.getHost(), inst.getPort(), allowedTenants); + return true; } - return match; + return false; }) .collect(Collectors.toList()); @@ -169,21 +173,18 @@ public class TenantAwareLoadBalancer implements ReactorServiceInstanceLoadBalanc } // 检查是否启用回退到共享实例 - boolean fallbackEnabled = true; - if (routingProperties != null) { - fallbackEnabled = routingProperties.isFallbackToShared(); - } + boolean fallbackEnabled = routingProperties == null || routingProperties.isFallbackToShared(); if (!fallbackEnabled) { logger.warn("[TenantLB] 服务 {} 未启用共享实例回退,返回空列表", serviceId); - return List.of(); + return Collections.emptyList(); } // 回退到共享实例(metadata.tenant-id 为空或不存在) List sharedInstances = instances.stream() .filter(inst -> { - Map metadata = inst.getMetadata(); - return metadata == null || metadata.get("tenant-id") == null || metadata.get("tenant-id").isEmpty(); + List allowedTenants = parseTenantIds(inst); + return allowedTenants.isEmpty(); // 空列表表示共享实例 }) .collect(Collectors.toList()); @@ -191,6 +192,30 @@ public class TenantAwareLoadBalancer implements ReactorServiceInstanceLoadBalanc return sharedInstances; } + /** + * 解析实例元数据中的租户 ID 列表 + * + * @param inst 服务实例 + * @return 租户 ID 列表,空列表表示共享实例 + */ + private List parseTenantIds(ServiceInstance inst) { + Map metadata = inst.getMetadata(); + if (metadata == null) { + return Collections.emptyList(); + } + + String tenantIdStr = metadata.get("tenant-id"); + if (tenantIdStr == null || tenantIdStr.trim().isEmpty()) { + return Collections.emptyList(); + } + + // 支持逗号分隔的多个租户 ID + return Arrays.stream(tenantIdStr.split(",")) + .map(String::trim) + .filter(s -> !s.isEmpty()) + .collect(Collectors.toList()); + } + /** * 从实例列表中选择一个(随机) */ diff --git a/fund-common/src/test/java/com/fundplatform/common/loadbalancer/TenantAwareLoadBalancerTest.java b/fund-common/src/test/java/com/fundplatform/common/loadbalancer/TenantAwareLoadBalancerTest.java index 90cacc7..ab4f87d 100644 --- a/fund-common/src/test/java/com/fundplatform/common/loadbalancer/TenantAwareLoadBalancerTest.java +++ b/fund-common/src/test/java/com/fundplatform/common/loadbalancer/TenantAwareLoadBalancerTest.java @@ -22,70 +22,140 @@ class TenantAwareLoadBalancerTest { } @Test - void testFilterByTenantId() { - // 创建测试实例 + void testSingleTenantInstance() { + // 单租户专属实例 List instances = Arrays.asList( createInstance("fund-sys", "192.168.1.1", 8100, Map.of("tenant-id", "VIP_001")), - createInstance("fund-sys", "192.168.1.2", 8101, Map.of("tenant-id", "VIP_001")), - createInstance("fund-sys", "192.168.1.3", 8102, Map.of("tenant-id", "VIP_002")), - createInstance("fund-sys", "192.168.1.4", 8103, Collections.emptyMap()) // 共享实例 + createInstance("fund-sys", "192.168.1.2", 8101, Map.of("tenant-id", "VIP_002")), + createInstance("fund-sys", "192.168.1.3", 8102, Collections.emptyMap()) // 共享实例 ); - // 测试 VIP_001 过滤 + // VIP_001 匹配专属实例 List vip1Instances = invokeFilterByTenantId(loadBalancer, instances, "VIP_001"); - assertEquals(2, vip1Instances.size()); - assertTrue(vip1Instances.stream().allMatch(i -> "VIP_001".equals(i.getMetadata().get("tenant-id")))); + assertEquals(1, vip1Instances.size()); + assertEquals("VIP_001", vip1Instances.get(0).getMetadata().get("tenant-id")); - // 测试 VIP_002 过滤 + // VIP_002 匹配专属实例 List vip2Instances = invokeFilterByTenantId(loadBalancer, instances, "VIP_002"); assertEquals(1, vip2Instances.size()); - // 测试未知租户(回退到共享实例) + // 未知租户回退到共享实例 List unknownInstances = invokeFilterByTenantId(loadBalancer, instances, "VIP_999"); assertEquals(1, unknownInstances.size()); assertFalse(unknownInstances.get(0).getMetadata().containsKey("tenant-id")); } @Test - void testMixedMode() { - // 混合模式:VIP 客户有专属实例,普通客户使用共享实例 + void testMultiTenantInstance() { + // 多租户专属实例(一个实例服务多个租户) List instances = Arrays.asList( - // VIP_001 的专属实例 - createInstance("fund-sys", "192.168.1.1", 8100, Map.of("tenant-id", "VIP_001")), - createInstance("fund-sys", "192.168.1.2", 8101, Map.of("tenant-id", "VIP_001")), - // VIP_002 的专属实例 - createInstance("fund-sys", "192.168.1.3", 8102, Map.of("tenant-id", "VIP_002")), - // 共享实例(普通租户使用) - createInstance("fund-sys", "192.168.1.10", 8110, Collections.emptyMap()), - createInstance("fund-sys", "192.168.1.11", 8111, Collections.emptyMap()) + // 实例1:服务 VIP_001, VIP_002, VIP_003 + createInstance("fund-sys", "192.168.1.1", 8100, Map.of("tenant-id", "VIP_001,VIP_002,VIP_003")), + // 实例2:服务 VIP_004, VIP_005 + createInstance("fund-sys", "192.168.1.2", 8101, Map.of("tenant-id", "VIP_004,VIP_005")), + // 实例3:共享实例 + createInstance("fund-sys", "192.168.1.10", 8110, Collections.emptyMap()) ); - // VIP_001 应该路由到专属实例 + // VIP_001 匹配实例1 List vip1Instances = invokeFilterByTenantId(loadBalancer, instances, "VIP_001"); - assertEquals(2, vip1Instances.size()); + assertEquals(1, vip1Instances.size()); + assertEquals("8100", String.valueOf(vip1Instances.get(0).getPort())); - // VIP_002 应该路由到专属实例 + // VIP_003 也匹配实例1 + List vip3Instances = invokeFilterByTenantId(loadBalancer, instances, "VIP_003"); + assertEquals(1, vip3Instances.size()); + assertEquals("8100", String.valueOf(vip3Instances.get(0).getPort())); + + // VIP_004 匹配实例2 + List vip4Instances = invokeFilterByTenantId(loadBalancer, instances, "VIP_004"); + assertEquals(1, vip4Instances.size()); + assertEquals("8101", String.valueOf(vip4Instances.get(0).getPort())); + + // VIP_005 匹配实例2 + List vip5Instances = invokeFilterByTenantId(loadBalancer, instances, "VIP_005"); + assertEquals(1, vip5Instances.size()); + assertEquals("8101", String.valueOf(vip5Instances.get(0).getPort())); + + // 未知租户回退到共享实例 + List unknownInstances = invokeFilterByTenantId(loadBalancer, instances, "VIP_999"); + assertEquals(1, unknownInstances.size()); + assertEquals("8110", String.valueOf(unknownInstances.get(0).getPort())); + } + + @Test + void testMixedMode() { + // 混合模式:单租户实例 + 多租户实例 + 共享实例 + List instances = Arrays.asList( + // 单租户专属实例 + createInstance("fund-sys", "192.168.1.1", 8100, Map.of("tenant-id", "VIP_001")), + // 多租户专属实例(VIP_002, VIP_003) + createInstance("fund-sys", "192.168.1.2", 8101, Map.of("tenant-id", "VIP_002,VIP_003")), + // 共享实例 + createInstance("fund-sys", "192.168.1.10", 8110, Collections.emptyMap()) + ); + + // VIP_001 走单租户实例 + List vip1Instances = invokeFilterByTenantId(loadBalancer, instances, "VIP_001"); + assertEquals(1, vip1Instances.size()); + assertEquals(8100, vip1Instances.get(0).getPort()); + + // VIP_002 走多租户实例 List vip2Instances = invokeFilterByTenantId(loadBalancer, instances, "VIP_002"); assertEquals(1, vip2Instances.size()); + assertEquals(8101, vip2Instances.get(0).getPort()); - // 普通租户应该路由到共享实例 + // VIP_003 也走多租户实例 + List vip3Instances = invokeFilterByTenantId(loadBalancer, instances, "VIP_003"); + assertEquals(1, vip3Instances.size()); + assertEquals(8101, vip3Instances.get(0).getPort()); + + // 普通租户走共享实例 List normalInstances = invokeFilterByTenantId(loadBalancer, instances, "NORMAL_001"); - assertEquals(2, normalInstances.size()); + assertEquals(1, normalInstances.size()); + assertEquals(8110, normalInstances.get(0).getPort()); + } + + @Test + void testSharedInstanceFallback() { + // 测试回退到共享实例 + List instances = Arrays.asList( + createInstance("fund-sys", "192.168.1.1", 8100, Map.of("tenant-id", "VIP_001")), + createInstance("fund-sys", "192.168.1.2", 8101, Collections.emptyMap()), + createInstance("fund-sys", "192.168.1.3", 8102, Map.of("tenant-id", "")) // 空字符串也是共享 + ); + + // 未知租户应该找到 2 个共享实例 + List sharedInstances = invokeFilterByTenantId(loadBalancer, instances, "UNKNOWN"); + assertEquals(2, sharedInstances.size()); } @Test void testNullTenantId() { - // 测试空租户 ID(应该回退到共享实例) List instances = Arrays.asList( createInstance("fund-sys", "192.168.1.1", 8100, Map.of("tenant-id", "VIP_001")), createInstance("fund-sys", "192.168.1.2", 8101, Collections.emptyMap()) ); + // null 租户 ID 应该回退到共享实例 List result = invokeFilterByTenantId(loadBalancer, instances, null); assertEquals(1, result.size()); assertFalse(result.get(0).getMetadata().containsKey("tenant-id")); } + @Test + void testTenantIdWithSpaces() { + // 测试带空格的租户 ID 配置 + List instances = Arrays.asList( + createInstance("fund-sys", "192.168.1.1", 8100, Map.of("tenant-id", "VIP_001, VIP_002 , VIP_003")) + ); + + // 应该能正确匹配(去除空格) + assertEquals(1, invokeFilterByTenantId(loadBalancer, instances, "VIP_001").size()); + assertEquals(1, invokeFilterByTenantId(loadBalancer, instances, "VIP_002").size()); + assertEquals(1, invokeFilterByTenantId(loadBalancer, instances, "VIP_003").size()); + } + // 辅助方法:创建服务实例 private ServiceInstance createInstance(String serviceId, String host, int port, Map metadata) { return new DefaultServiceInstance( diff --git a/fund-common/target/classes/com/fundplatform/common/loadbalancer/TenantAwareLoadBalancer.class b/fund-common/target/classes/com/fundplatform/common/loadbalancer/TenantAwareLoadBalancer.class index 09a93da7d90a73d9cd1de038a8d0ee98f1405afe..52fe28ddeb640378ab5e8b02be56c1439c3d2470 100644 GIT binary patch delta 2936 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 diff --git a/fund-common/target/surefire-reports/TEST-com.fundplatform.common.loadbalancer.TenantAwareLoadBalancerTest.xml b/fund-common/target/surefire-reports/TEST-com.fundplatform.common.loadbalancer.TenantAwareLoadBalancerTest.xml index d2d15a3..40bef56 100644 --- a/fund-common/target/surefire-reports/TEST-com.fundplatform.common.loadbalancer.TenantAwareLoadBalancerTest.xml +++ b/fund-common/target/surefire-reports/TEST-com.fundplatform.common.loadbalancer.TenantAwareLoadBalancerTest.xml @@ -1,5 +1,5 @@ - + @@ -13,7 +13,7 @@ - + @@ -29,7 +29,7 @@ - + @@ -56,20 +56,39 @@ - - + - - + - - + + + + + + + + + + \ No newline at end of file diff --git a/fund-common/target/surefire-reports/com.fundplatform.common.loadbalancer.TenantAwareLoadBalancerTest.txt b/fund-common/target/surefire-reports/com.fundplatform.common.loadbalancer.TenantAwareLoadBalancerTest.txt index 0d24cd0..5c9c301 100644 --- a/fund-common/target/surefire-reports/com.fundplatform.common.loadbalancer.TenantAwareLoadBalancerTest.txt +++ b/fund-common/target/surefire-reports/com.fundplatform.common.loadbalancer.TenantAwareLoadBalancerTest.txt @@ -1,4 +1,4 @@ ------------------------------------------------------------------------------- Test set: com.fundplatform.common.loadbalancer.TenantAwareLoadBalancerTest ------------------------------------------------------------------------------- -Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.206 s -- in com.fundplatform.common.loadbalancer.TenantAwareLoadBalancerTest +Tests run: 6, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.505 s -- in com.fundplatform.common.loadbalancer.TenantAwareLoadBalancerTest diff --git a/fund-common/target/test-classes/com/fundplatform/common/loadbalancer/TenantAwareLoadBalancerTest.class b/fund-common/target/test-classes/com/fundplatform/common/loadbalancer/TenantAwareLoadBalancerTest.class index ef1af4dd2c4650f910cd3c7d8459e58684c1124e..6493fa444bb2bfb133a76bc5c8991602bdd1aeb9 100644 GIT binary patch literal 6503 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 af92kvNc%J1!Qb$AQe;E@2mXcspyvN`+#ovu diff --git a/logs/fund-sys-shared-8100.log b/logs/fund-sys-shared-8100.log index 87cc114..bbe95d6 100644 --- a/logs/fund-sys-shared-8100.log +++ b/logs/fund-sys-shared-8100.log @@ -1174,3 +1174,21 @@ java.lang.NullPointerException: Cannot invoke "com.zaxxer.hikari.HikariPoolMXBea at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) at java.base/java.lang.Thread.run(Thread.java:1583) +2026-02-19 21:36:06.988 [scheduling-1] [] INFO com.fundplatform.sys.config.HikariMonitorConfig - === HikariCP 连接池状态 === +2026-02-19 21:36:06.988 [scheduling-1] [] INFO com.fundplatform.sys.config.HikariMonitorConfig - 连接池名称: FundSysHikariPool +2026-02-19 21:36:06.989 [scheduling-1] [] ERROR o.s.s.support.TaskUtils$LoggingErrorHandler - Unexpected error occurred in scheduled task +java.lang.NullPointerException: Cannot invoke "com.zaxxer.hikari.HikariPoolMXBean.getActiveConnections()" because the return value of "com.zaxxer.hikari.HikariDataSource.getHikariPoolMXBean()" is null + at com.fundplatform.sys.config.HikariMonitorConfig.monitorHikariPool(HikariMonitorConfig.java:38) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.scheduling.support.ScheduledMethodRunnable.runInternal(ScheduledMethodRunnable.java:130) + at org.springframework.scheduling.support.ScheduledMethodRunnable.lambda$run$2(ScheduledMethodRunnable.java:124) + at io.micrometer.observation.Observation.observe(Observation.java:499) + at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:124) + at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) + at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572) + at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:358) + at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) + at java.base/java.lang.Thread.run(Thread.java:1583) diff --git a/logs/fund-sys-tenant-vip001-8101.log b/logs/fund-sys-tenant-vip001-8101.log index 1bd03c0..395045c 100644 --- a/logs/fund-sys-tenant-vip001-8101.log +++ b/logs/fund-sys-tenant-vip001-8101.log @@ -1138,3 +1138,39 @@ java.lang.NullPointerException: Cannot invoke "com.zaxxer.hikari.HikariPoolMXBea at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) at java.base/java.lang.Thread.run(Thread.java:1583) +2026-02-19 21:34:07.541 [scheduling-1] [] INFO com.fundplatform.sys.config.HikariMonitorConfig - === HikariCP 连接池状态 === +2026-02-19 21:34:07.541 [scheduling-1] [] INFO com.fundplatform.sys.config.HikariMonitorConfig - 连接池名称: FundSysHikariPool +2026-02-19 21:34:07.541 [scheduling-1] [] ERROR o.s.s.support.TaskUtils$LoggingErrorHandler - Unexpected error occurred in scheduled task +java.lang.NullPointerException: Cannot invoke "com.zaxxer.hikari.HikariPoolMXBean.getActiveConnections()" because the return value of "com.zaxxer.hikari.HikariDataSource.getHikariPoolMXBean()" is null + at com.fundplatform.sys.config.HikariMonitorConfig.monitorHikariPool(HikariMonitorConfig.java:38) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.scheduling.support.ScheduledMethodRunnable.runInternal(ScheduledMethodRunnable.java:130) + at org.springframework.scheduling.support.ScheduledMethodRunnable.lambda$run$2(ScheduledMethodRunnable.java:124) + at io.micrometer.observation.Observation.observe(Observation.java:499) + at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:124) + at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) + at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572) + at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:358) + at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) + at java.base/java.lang.Thread.run(Thread.java:1583) +2026-02-19 21:39:07.541 [scheduling-1] [] INFO com.fundplatform.sys.config.HikariMonitorConfig - === HikariCP 连接池状态 === +2026-02-19 21:39:07.541 [scheduling-1] [] INFO com.fundplatform.sys.config.HikariMonitorConfig - 连接池名称: FundSysHikariPool +2026-02-19 21:39:07.541 [scheduling-1] [] ERROR o.s.s.support.TaskUtils$LoggingErrorHandler - Unexpected error occurred in scheduled task +java.lang.NullPointerException: Cannot invoke "com.zaxxer.hikari.HikariPoolMXBean.getActiveConnections()" because the return value of "com.zaxxer.hikari.HikariDataSource.getHikariPoolMXBean()" is null + at com.fundplatform.sys.config.HikariMonitorConfig.monitorHikariPool(HikariMonitorConfig.java:38) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.scheduling.support.ScheduledMethodRunnable.runInternal(ScheduledMethodRunnable.java:130) + at org.springframework.scheduling.support.ScheduledMethodRunnable.lambda$run$2(ScheduledMethodRunnable.java:124) + at io.micrometer.observation.Observation.observe(Observation.java:499) + at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:124) + at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) + at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572) + at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:358) + at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) + at java.base/java.lang.Thread.run(Thread.java:1583) diff --git a/logs/fund-sys/error.log b/logs/fund-sys/error.log index 53e79df..1cad01a 100644 --- a/logs/fund-sys/error.log +++ b/logs/fund-sys/error.log @@ -1566,3 +1566,51 @@ java.lang.NullPointerException: Cannot invoke "com.zaxxer.hikari.HikariPoolMXBea at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) at java.base/java.lang.Thread.run(Thread.java:1583) +2026-02-19 21:34:07.541 [scheduling-1] [] ERROR o.s.s.support.TaskUtils$LoggingErrorHandler - Unexpected error occurred in scheduled task +java.lang.NullPointerException: Cannot invoke "com.zaxxer.hikari.HikariPoolMXBean.getActiveConnections()" because the return value of "com.zaxxer.hikari.HikariDataSource.getHikariPoolMXBean()" is null + at com.fundplatform.sys.config.HikariMonitorConfig.monitorHikariPool(HikariMonitorConfig.java:38) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.scheduling.support.ScheduledMethodRunnable.runInternal(ScheduledMethodRunnable.java:130) + at org.springframework.scheduling.support.ScheduledMethodRunnable.lambda$run$2(ScheduledMethodRunnable.java:124) + at io.micrometer.observation.Observation.observe(Observation.java:499) + at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:124) + at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) + at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572) + at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:358) + at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) + at java.base/java.lang.Thread.run(Thread.java:1583) +2026-02-19 21:36:06.989 [scheduling-1] [] ERROR o.s.s.support.TaskUtils$LoggingErrorHandler - Unexpected error occurred in scheduled task +java.lang.NullPointerException: Cannot invoke "com.zaxxer.hikari.HikariPoolMXBean.getActiveConnections()" because the return value of "com.zaxxer.hikari.HikariDataSource.getHikariPoolMXBean()" is null + at com.fundplatform.sys.config.HikariMonitorConfig.monitorHikariPool(HikariMonitorConfig.java:38) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.scheduling.support.ScheduledMethodRunnable.runInternal(ScheduledMethodRunnable.java:130) + at org.springframework.scheduling.support.ScheduledMethodRunnable.lambda$run$2(ScheduledMethodRunnable.java:124) + at io.micrometer.observation.Observation.observe(Observation.java:499) + at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:124) + at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) + at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572) + at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:358) + at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) + at java.base/java.lang.Thread.run(Thread.java:1583) +2026-02-19 21:39:07.541 [scheduling-1] [] ERROR o.s.s.support.TaskUtils$LoggingErrorHandler - Unexpected error occurred in scheduled task +java.lang.NullPointerException: Cannot invoke "com.zaxxer.hikari.HikariPoolMXBean.getActiveConnections()" because the return value of "com.zaxxer.hikari.HikariDataSource.getHikariPoolMXBean()" is null + at com.fundplatform.sys.config.HikariMonitorConfig.monitorHikariPool(HikariMonitorConfig.java:38) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.scheduling.support.ScheduledMethodRunnable.runInternal(ScheduledMethodRunnable.java:130) + at org.springframework.scheduling.support.ScheduledMethodRunnable.lambda$run$2(ScheduledMethodRunnable.java:124) + at io.micrometer.observation.Observation.observe(Observation.java:499) + at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:124) + at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) + at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572) + at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:358) + at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) + at java.base/java.lang.Thread.run(Thread.java:1583) diff --git a/logs/fund-sys/info.log b/logs/fund-sys/info.log index 1698e7a..13c83f5 100644 --- a/logs/fund-sys/info.log +++ b/logs/fund-sys/info.log @@ -340,3 +340,9 @@ 2026-02-19 21:29:07.541 [scheduling-1] [] INFO com.fundplatform.sys.config.HikariMonitorConfig - 连接池名称: FundSysHikariPool 2026-02-19 21:31:06.988 [scheduling-1] [] INFO com.fundplatform.sys.config.HikariMonitorConfig - === HikariCP 连接池状态 === 2026-02-19 21:31:06.988 [scheduling-1] [] INFO com.fundplatform.sys.config.HikariMonitorConfig - 连接池名称: FundSysHikariPool +2026-02-19 21:34:07.541 [scheduling-1] [] INFO com.fundplatform.sys.config.HikariMonitorConfig - === HikariCP 连接池状态 === +2026-02-19 21:34:07.541 [scheduling-1] [] INFO com.fundplatform.sys.config.HikariMonitorConfig - 连接池名称: FundSysHikariPool +2026-02-19 21:36:06.988 [scheduling-1] [] INFO com.fundplatform.sys.config.HikariMonitorConfig - === HikariCP 连接池状态 === +2026-02-19 21:36:06.988 [scheduling-1] [] INFO com.fundplatform.sys.config.HikariMonitorConfig - 连接池名称: FundSysHikariPool +2026-02-19 21:39:07.541 [scheduling-1] [] INFO com.fundplatform.sys.config.HikariMonitorConfig - === HikariCP 连接池状态 === +2026-02-19 21:39:07.541 [scheduling-1] [] INFO com.fundplatform.sys.config.HikariMonitorConfig - 连接池名称: FundSysHikariPool