feat: 支持多租户专属实例(逗号分隔的租户ID列表)
功能增强: - 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. 匹配失败 → 回退共享实例 使用场景: - 大客户独占实例(单租户) - 多个小客户共享一个实例(多租户) - 普通客户使用公共实例(共享)
This commit is contained in:
parent
5a2154c1a1
commit
8233ff8040
@ -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:
|
||||
|
||||
@ -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;
|
||||
*
|
||||
* <h3>路由规则:</h3>
|
||||
* <pre>
|
||||
* 1. 查找 metadata.tenant-id == 请求.tenantId 的实例 → VIP 专属实例
|
||||
* 1. 查找 metadata.tenant-id 包含请求 tenantId 的实例 → 专属实例
|
||||
* 2. 找不到 → 回退到共享实例(metadata.tenant-id 为空)
|
||||
* </pre>
|
||||
*
|
||||
* <h3>实例配置:</h3>
|
||||
* <pre>
|
||||
* 共享实例: 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"
|
||||
* </pre>
|
||||
*/
|
||||
public class TenantAwareLoadBalancer implements ReactorServiceInstanceLoadBalancer {
|
||||
@ -130,9 +133,11 @@ public class TenantAwareLoadBalancer implements ReactorServiceInstanceLoadBalanc
|
||||
*
|
||||
* <p>路由策略:</p>
|
||||
* <ol>
|
||||
* <li>优先选择租户专属实例(metadata.tenant-id 匹配)</li>
|
||||
* <li>优先选择租户专属实例(metadata.tenant-id 包含请求的 tenantId)</li>
|
||||
* <li>回退到共享实例(metadata.tenant-id 为空或不存在)</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>tenant-id 支持逗号分隔的多个租户 ID</p>
|
||||
*/
|
||||
List<ServiceInstance> filterByTenantId(List<ServiceInstance> instances, String tenantId) {
|
||||
if (instances == null || instances.isEmpty()) {
|
||||
@ -151,14 +156,13 @@ public class TenantAwareLoadBalancer implements ReactorServiceInstanceLoadBalanc
|
||||
if (tenantId != null && !tenantId.isEmpty()) {
|
||||
List<ServiceInstance> tenantInstances = instances.stream()
|
||||
.filter(inst -> {
|
||||
Map<String, String> 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<String> 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<ServiceInstance> sharedInstances = instances.stream()
|
||||
.filter(inst -> {
|
||||
Map<String, String> metadata = inst.getMetadata();
|
||||
return metadata == null || metadata.get("tenant-id") == null || metadata.get("tenant-id").isEmpty();
|
||||
List<String> 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<String> parseTenantIds(ServiceInstance inst) {
|
||||
Map<String, String> 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());
|
||||
}
|
||||
|
||||
/**
|
||||
* 从实例列表中选择一个(随机)
|
||||
*/
|
||||
|
||||
@ -22,70 +22,140 @@ class TenantAwareLoadBalancerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFilterByTenantId() {
|
||||
// 创建测试实例
|
||||
void testSingleTenantInstance() {
|
||||
// 单租户专属实例
|
||||
List<ServiceInstance> 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<ServiceInstance> 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<ServiceInstance> vip2Instances = invokeFilterByTenantId(loadBalancer, instances, "VIP_002");
|
||||
assertEquals(1, vip2Instances.size());
|
||||
|
||||
// 测试未知租户(回退到共享实例)
|
||||
// 未知租户回退到共享实例
|
||||
List<ServiceInstance> 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<ServiceInstance> 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<ServiceInstance> 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<ServiceInstance> vip3Instances = invokeFilterByTenantId(loadBalancer, instances, "VIP_003");
|
||||
assertEquals(1, vip3Instances.size());
|
||||
assertEquals("8100", String.valueOf(vip3Instances.get(0).getPort()));
|
||||
|
||||
// VIP_004 匹配实例2
|
||||
List<ServiceInstance> vip4Instances = invokeFilterByTenantId(loadBalancer, instances, "VIP_004");
|
||||
assertEquals(1, vip4Instances.size());
|
||||
assertEquals("8101", String.valueOf(vip4Instances.get(0).getPort()));
|
||||
|
||||
// VIP_005 匹配实例2
|
||||
List<ServiceInstance> vip5Instances = invokeFilterByTenantId(loadBalancer, instances, "VIP_005");
|
||||
assertEquals(1, vip5Instances.size());
|
||||
assertEquals("8101", String.valueOf(vip5Instances.get(0).getPort()));
|
||||
|
||||
// 未知租户回退到共享实例
|
||||
List<ServiceInstance> unknownInstances = invokeFilterByTenantId(loadBalancer, instances, "VIP_999");
|
||||
assertEquals(1, unknownInstances.size());
|
||||
assertEquals("8110", String.valueOf(unknownInstances.get(0).getPort()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMixedMode() {
|
||||
// 混合模式:单租户实例 + 多租户实例 + 共享实例
|
||||
List<ServiceInstance> 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<ServiceInstance> vip1Instances = invokeFilterByTenantId(loadBalancer, instances, "VIP_001");
|
||||
assertEquals(1, vip1Instances.size());
|
||||
assertEquals(8100, vip1Instances.get(0).getPort());
|
||||
|
||||
// VIP_002 走多租户实例
|
||||
List<ServiceInstance> vip2Instances = invokeFilterByTenantId(loadBalancer, instances, "VIP_002");
|
||||
assertEquals(1, vip2Instances.size());
|
||||
assertEquals(8101, vip2Instances.get(0).getPort());
|
||||
|
||||
// 普通租户应该路由到共享实例
|
||||
// VIP_003 也走多租户实例
|
||||
List<ServiceInstance> vip3Instances = invokeFilterByTenantId(loadBalancer, instances, "VIP_003");
|
||||
assertEquals(1, vip3Instances.size());
|
||||
assertEquals(8101, vip3Instances.get(0).getPort());
|
||||
|
||||
// 普通租户走共享实例
|
||||
List<ServiceInstance> normalInstances = invokeFilterByTenantId(loadBalancer, instances, "NORMAL_001");
|
||||
assertEquals(2, normalInstances.size());
|
||||
assertEquals(1, normalInstances.size());
|
||||
assertEquals(8110, normalInstances.get(0).getPort());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSharedInstanceFallback() {
|
||||
// 测试回退到共享实例
|
||||
List<ServiceInstance> 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<ServiceInstance> sharedInstances = invokeFilterByTenantId(loadBalancer, instances, "UNKNOWN");
|
||||
assertEquals(2, sharedInstances.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNullTenantId() {
|
||||
// 测试空租户 ID(应该回退到共享实例)
|
||||
List<ServiceInstance> 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<ServiceInstance> result = invokeFilterByTenantId(loadBalancer, instances, null);
|
||||
assertEquals(1, result.size());
|
||||
assertFalse(result.get(0).getMetadata().containsKey("tenant-id"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testTenantIdWithSpaces() {
|
||||
// 测试带空格的租户 ID 配置
|
||||
List<ServiceInstance> 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<String, String> metadata) {
|
||||
return new DefaultServiceInstance(
|
||||
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
@ -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
|
||||
|
||||
Binary file not shown.
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user