|
@@ -9,6 +9,7 @@ import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
|
|
import lombok.Data;
|
|
import lombok.Data;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
import org.springframework.stereotype.Component;
|
|
import org.springframework.stereotype.Component;
|
|
|
|
|
|
|
|
import javax.annotation.PostConstruct;
|
|
import javax.annotation.PostConstruct;
|
|
@@ -46,8 +47,8 @@ public class DynamicThreadPoolManager {
|
|
|
* Apollo 配置的线程池参数
|
|
* Apollo 配置的线程池参数
|
|
|
* 配置格式:
|
|
* 配置格式:
|
|
|
* [
|
|
* [
|
|
|
- * {"poolName":"DEFAULT","corePoolSize":32,"maxPoolSize":64,"queueCapacity":3000,"keepAliveSeconds":60,"rejectedPolicy":"CALLER_RUNS"},
|
|
|
|
|
- * {"poolName":"MULTI_GET_FEATURE","corePoolSize":64,"maxPoolSize":128,"queueCapacity":5000,"keepAliveSeconds":60,"rejectedPolicy":"CALLER_RUNS"}
|
|
|
|
|
|
|
+ * {"poolName":"DEFAULT","corePoolSize":8,"maxPoolSize":16,"queueCapacity":1000,"keepAliveSeconds":60,"rejectedPolicy":"CALLER_RUNS"},
|
|
|
|
|
+ * {"poolName":"MULTI_GET_FEATURE","corePoolSize":16,"maxPoolSize":32,"queueCapacity":1800,"keepAliveSeconds":60,"rejectedPolicy":"CALLER_RUNS"}
|
|
|
* ]
|
|
* ]
|
|
|
*/
|
|
*/
|
|
|
@ApolloJsonValue("${thread.pool.configs:[]}")
|
|
@ApolloJsonValue("${thread.pool.configs:[]}")
|
|
@@ -65,7 +66,7 @@ public class DynamicThreadPoolManager {
|
|
|
DEFAULT_CONFIG.setPoolName(DEFAULT_POOL);
|
|
DEFAULT_CONFIG.setPoolName(DEFAULT_POOL);
|
|
|
DEFAULT_CONFIG.setCorePoolSize(8);
|
|
DEFAULT_CONFIG.setCorePoolSize(8);
|
|
|
DEFAULT_CONFIG.setMaxPoolSize(16);
|
|
DEFAULT_CONFIG.setMaxPoolSize(16);
|
|
|
- DEFAULT_CONFIG.setQueueCapacity(3000);
|
|
|
|
|
|
|
+ DEFAULT_CONFIG.setQueueCapacity(1000);
|
|
|
DEFAULT_CONFIG.setKeepAliveSeconds(60);
|
|
DEFAULT_CONFIG.setKeepAliveSeconds(60);
|
|
|
DEFAULT_CONFIG.setRejectedPolicy("CALLER_RUNS");
|
|
DEFAULT_CONFIG.setRejectedPolicy("CALLER_RUNS");
|
|
|
|
|
|
|
@@ -74,11 +75,31 @@ public class DynamicThreadPoolManager {
|
|
|
MULTI_GET_FEATURE_CONFIG.setPoolName(MULTI_GET_FEATURE_POOL);
|
|
MULTI_GET_FEATURE_CONFIG.setPoolName(MULTI_GET_FEATURE_POOL);
|
|
|
MULTI_GET_FEATURE_CONFIG.setCorePoolSize(16);
|
|
MULTI_GET_FEATURE_CONFIG.setCorePoolSize(16);
|
|
|
MULTI_GET_FEATURE_CONFIG.setMaxPoolSize(32);
|
|
MULTI_GET_FEATURE_CONFIG.setMaxPoolSize(32);
|
|
|
- MULTI_GET_FEATURE_CONFIG.setQueueCapacity(5000);
|
|
|
|
|
|
|
+ MULTI_GET_FEATURE_CONFIG.setQueueCapacity(1800);
|
|
|
MULTI_GET_FEATURE_CONFIG.setKeepAliveSeconds(60);
|
|
MULTI_GET_FEATURE_CONFIG.setKeepAliveSeconds(60);
|
|
|
MULTI_GET_FEATURE_CONFIG.setRejectedPolicy("CALLER_RUNS");
|
|
MULTI_GET_FEATURE_CONFIG.setRejectedPolicy("CALLER_RUNS");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 监控阈值配置(可通过 Apollo 动态调整)
|
|
|
|
|
+ */
|
|
|
|
|
+ @Value("${thread.pool.monitor.enabled:true}")
|
|
|
|
|
+ private boolean monitorEnabled;
|
|
|
|
|
+
|
|
|
|
|
+ @Value("${thread.pool.monitor.interval:30}")
|
|
|
|
|
+ private int monitorIntervalSeconds;
|
|
|
|
|
+
|
|
|
|
|
+ @Value("${thread.pool.monitor.thread.threshold:0.8}")
|
|
|
|
|
+ private double threadUsageThreshold;
|
|
|
|
|
+
|
|
|
|
|
+ @Value("${thread.pool.monitor.queue.threshold:0.8}")
|
|
|
|
|
+ private double queueUsageThreshold;
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 定时监控调度器
|
|
|
|
|
+ */
|
|
|
|
|
+ private ScheduledExecutorService monitorScheduler;
|
|
|
|
|
+
|
|
|
@PostConstruct
|
|
@PostConstruct
|
|
|
public void init() {
|
|
public void init() {
|
|
|
// 初始化默认线程池
|
|
// 初始化默认线程池
|
|
@@ -93,6 +114,93 @@ public class DynamicThreadPoolManager {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
log.info("DynamicThreadPoolManager initialized, pools: {}", threadPoolRegistry.keySet());
|
|
log.info("DynamicThreadPoolManager initialized, pools: {}", threadPoolRegistry.keySet());
|
|
|
|
|
+
|
|
|
|
|
+ // 启动定时监控任务
|
|
|
|
|
+ startMonitor();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 启动线程池监控任务
|
|
|
|
|
+ */
|
|
|
|
|
+ private void startMonitor() {
|
|
|
|
|
+ if (!monitorEnabled) {
|
|
|
|
|
+ log.info("Thread pool monitor is disabled");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ monitorScheduler = Executors.newSingleThreadScheduledExecutor(
|
|
|
|
|
+ new ThreadFactoryBuilder().setNameFormat("ThreadPoolMonitor-%d").setDaemon(true).build()
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ monitorScheduler.scheduleAtFixedRate(this::checkThreadPoolStatus,
|
|
|
|
|
+ monitorIntervalSeconds, monitorIntervalSeconds, TimeUnit.SECONDS);
|
|
|
|
|
+
|
|
|
|
|
+ log.info("Thread pool monitor started, interval={}s, threadThreshold={}%, queueThreshold={}%",
|
|
|
|
|
+ monitorIntervalSeconds, (int)(threadUsageThreshold * 100), (int)(queueUsageThreshold * 100));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 检查线程池状态,超过阈值时输出告警
|
|
|
|
|
+ */
|
|
|
|
|
+ private void checkThreadPoolStatus() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ for (Map.Entry<String, ThreadPoolExecutor> entry : threadPoolRegistry.entrySet()) {
|
|
|
|
|
+ String poolName = entry.getKey();
|
|
|
|
|
+ ThreadPoolExecutor executor = entry.getValue();
|
|
|
|
|
+
|
|
|
|
|
+ int activeCount = executor.getActiveCount();
|
|
|
|
|
+ int maxPoolSize = executor.getMaximumPoolSize();
|
|
|
|
|
+ int queueSize = executor.getQueue().size();
|
|
|
|
|
+ int queueCapacity = getQueueCapacity(poolName);
|
|
|
|
|
+
|
|
|
|
|
+ double threadUsage = (double) activeCount / maxPoolSize;
|
|
|
|
|
+ double queueUsage = queueCapacity > 0 ? (double) queueSize / queueCapacity : 0;
|
|
|
|
|
+
|
|
|
|
|
+ // 线程使用率超过阈值
|
|
|
|
|
+ if (threadUsage >= threadUsageThreshold) {
|
|
|
|
|
+ log.warn("[ThreadPool ALERT] [{}] 线程使用率过高! activeCount={}/{} ({}%), " +
|
|
|
|
|
+ "poolSize={}, queueSize={}/{}, completedTasks={}, totalTasks={}",
|
|
|
|
|
+ poolName, activeCount, maxPoolSize, (int)(threadUsage * 100),
|
|
|
|
|
+ executor.getPoolSize(), queueSize, queueCapacity,
|
|
|
|
|
+ executor.getCompletedTaskCount(), executor.getTaskCount());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 队列使用率超过阈值
|
|
|
|
|
+ if (queueUsage >= queueUsageThreshold) {
|
|
|
|
|
+ log.warn("[ThreadPool ALERT] [{}] 队列使用率过高! queueSize={}/{} ({}%), " +
|
|
|
|
|
+ "activeCount={}/{}, poolSize={}, completedTasks={}, totalTasks={}",
|
|
|
|
|
+ poolName, queueSize, queueCapacity, (int)(queueUsage * 100),
|
|
|
|
|
+ activeCount, maxPoolSize, executor.getPoolSize(),
|
|
|
|
|
+ executor.getCompletedTaskCount(), executor.getTaskCount());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("Thread pool monitor error", e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 手动输出所有线程池当前状态(可用于排查问题)
|
|
|
|
|
+ */
|
|
|
|
|
+ public void printAllPoolStatus() {
|
|
|
|
|
+ log.info("===== Thread Pool Status Report =====");
|
|
|
|
|
+ for (Map.Entry<String, ThreadPoolExecutor> entry : threadPoolRegistry.entrySet()) {
|
|
|
|
|
+ String poolName = entry.getKey();
|
|
|
|
|
+ ThreadPoolExecutor executor = entry.getValue();
|
|
|
|
|
+ int queueCapacity = getQueueCapacity(poolName);
|
|
|
|
|
+
|
|
|
|
|
+ log.info("[{}] coreSize={}, maxSize={}, poolSize={}, activeCount={}, " +
|
|
|
|
|
+ "queueSize={}/{}, completedTasks={}, totalTasks={}",
|
|
|
|
|
+ poolName,
|
|
|
|
|
+ executor.getCorePoolSize(),
|
|
|
|
|
+ executor.getMaximumPoolSize(),
|
|
|
|
|
+ executor.getPoolSize(),
|
|
|
|
|
+ executor.getActiveCount(),
|
|
|
|
|
+ executor.getQueue().size(), queueCapacity,
|
|
|
|
|
+ executor.getCompletedTaskCount(),
|
|
|
|
|
+ executor.getTaskCount());
|
|
|
|
|
+ }
|
|
|
|
|
+ log.info("===== End of Report =====");
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -121,20 +229,71 @@ public class DynamicThreadPoolManager {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 获取拒绝策略
|
|
|
|
|
|
|
+ * 获取拒绝策略(带告警包装)
|
|
|
*/
|
|
*/
|
|
|
private RejectedExecutionHandler getRejectedExecutionHandler(String policy) {
|
|
private RejectedExecutionHandler getRejectedExecutionHandler(String policy) {
|
|
|
|
|
+ RejectedExecutionHandler originalHandler;
|
|
|
switch (policy.toUpperCase()) {
|
|
switch (policy.toUpperCase()) {
|
|
|
case "ABORT":
|
|
case "ABORT":
|
|
|
- return new ThreadPoolExecutor.AbortPolicy();
|
|
|
|
|
|
|
+ originalHandler = new ThreadPoolExecutor.AbortPolicy();
|
|
|
|
|
+ break;
|
|
|
case "DISCARD":
|
|
case "DISCARD":
|
|
|
- return new ThreadPoolExecutor.DiscardPolicy();
|
|
|
|
|
|
|
+ originalHandler = new ThreadPoolExecutor.DiscardPolicy();
|
|
|
|
|
+ break;
|
|
|
case "DISCARD_OLDEST":
|
|
case "DISCARD_OLDEST":
|
|
|
- return new ThreadPoolExecutor.DiscardOldestPolicy();
|
|
|
|
|
|
|
+ originalHandler = new ThreadPoolExecutor.DiscardOldestPolicy();
|
|
|
|
|
+ break;
|
|
|
case "CALLER_RUNS":
|
|
case "CALLER_RUNS":
|
|
|
default:
|
|
default:
|
|
|
- return new ThreadPoolExecutor.CallerRunsPolicy();
|
|
|
|
|
|
|
+ originalHandler = new ThreadPoolExecutor.CallerRunsPolicy();
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 包装原始策略,在任务被拒绝时实时输出告警
|
|
|
|
|
+ return new AlertingRejectedExecutionHandler(originalHandler, policy);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 带告警功能的拒绝策略包装器
|
|
|
|
|
+ * 当任务被拒绝时,实时输出线程池状态
|
|
|
|
|
+ */
|
|
|
|
|
+ private class AlertingRejectedExecutionHandler implements RejectedExecutionHandler {
|
|
|
|
|
+ private final RejectedExecutionHandler delegate;
|
|
|
|
|
+ private final String policyName;
|
|
|
|
|
+
|
|
|
|
|
+ public AlertingRejectedExecutionHandler(RejectedExecutionHandler delegate, String policyName) {
|
|
|
|
|
+ this.delegate = delegate;
|
|
|
|
|
+ this.policyName = policyName;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
|
|
|
|
|
+ // 实时输出告警:任务被拒绝
|
|
|
|
|
+ String poolName = getPoolNameByExecutor(executor);
|
|
|
|
|
+ int queueCapacity = getQueueCapacity(poolName);
|
|
|
|
|
+
|
|
|
|
|
+ log.error("[ThreadPool REJECTED] [{}] 任务被拒绝! 策略={}, " +
|
|
|
|
|
+ "activeCount={}/{}, poolSize={}, queueSize={}/{}, completedTasks={}, totalTasks={}",
|
|
|
|
|
+ poolName, policyName,
|
|
|
|
|
+ executor.getActiveCount(), executor.getMaximumPoolSize(),
|
|
|
|
|
+ executor.getPoolSize(),
|
|
|
|
|
+ executor.getQueue().size(), queueCapacity,
|
|
|
|
|
+ executor.getCompletedTaskCount(), executor.getTaskCount());
|
|
|
|
|
+
|
|
|
|
|
+ // 执行原始拒绝策略
|
|
|
|
|
+ delegate.rejectedExecution(r, executor);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 根据 executor 实例查找线程池名称
|
|
|
|
|
+ */
|
|
|
|
|
+ private String getPoolNameByExecutor(ThreadPoolExecutor executor) {
|
|
|
|
|
+ for (Map.Entry<String, ThreadPoolExecutor> entry : threadPoolRegistry.entrySet()) {
|
|
|
|
|
+ if (entry.getValue() == executor) {
|
|
|
|
|
+ return entry.getKey();
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
+ return "UNKNOWN";
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -286,12 +445,19 @@ public class DynamicThreadPoolManager {
|
|
|
} else if (MULTI_GET_FEATURE_POOL.equals(poolName)) {
|
|
} else if (MULTI_GET_FEATURE_POOL.equals(poolName)) {
|
|
|
return MULTI_GET_FEATURE_CONFIG.getQueueCapacity();
|
|
return MULTI_GET_FEATURE_CONFIG.getQueueCapacity();
|
|
|
}
|
|
}
|
|
|
- return 3000;
|
|
|
|
|
|
|
+ return DEFAULT_CONFIG.getQueueCapacity();
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@PreDestroy
|
|
@PreDestroy
|
|
|
public void shutdown() {
|
|
public void shutdown() {
|
|
|
log.info("Shutting down thread pools...");
|
|
log.info("Shutting down thread pools...");
|
|
|
|
|
+
|
|
|
|
|
+ // 先关闭监控调度器
|
|
|
|
|
+ if (monitorScheduler != null && !monitorScheduler.isShutdown()) {
|
|
|
|
|
+ monitorScheduler.shutdown();
|
|
|
|
|
+ log.info("Thread pool monitor shutdown");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
for (Map.Entry<String, ThreadPoolExecutor> entry : threadPoolRegistry.entrySet()) {
|
|
for (Map.Entry<String, ThreadPoolExecutor> entry : threadPoolRegistry.entrySet()) {
|
|
|
ThreadPoolExecutor executor = entry.getValue();
|
|
ThreadPoolExecutor executor = entry.getValue();
|
|
|
executor.shutdown();
|
|
executor.shutdown();
|