|
@@ -0,0 +1,545 @@
|
|
|
|
|
+package com.tzld.piaoquan.recommend.feature.common;
|
|
|
|
|
+
|
|
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
|
|
+import com.ctrip.framework.apollo.model.ConfigChange;
|
|
|
|
|
+import com.ctrip.framework.apollo.model.ConfigChangeEvent;
|
|
|
|
|
+import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;
|
|
|
|
|
+import com.ctrip.framework.apollo.spring.annotation.ApolloJsonValue;
|
|
|
|
|
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
|
|
|
|
+import lombok.Data;
|
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
+import org.apache.commons.lang3.StringUtils;
|
|
|
|
|
+import org.springframework.beans.factory.annotation.Value;
|
|
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
|
|
+
|
|
|
|
|
+import io.micrometer.core.instrument.Gauge;
|
|
|
|
|
+import io.micrometer.core.instrument.MeterRegistry;
|
|
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
|
|
+
|
|
|
|
|
+import javax.annotation.PostConstruct;
|
|
|
|
|
+import javax.annotation.PreDestroy;
|
|
|
|
|
+import java.util.List;
|
|
|
|
|
+import java.util.Map;
|
|
|
|
|
+import java.util.concurrent.*;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 动态线程池管理器
|
|
|
|
|
+ * 支持通过 Apollo 配置动态调整线程池参数
|
|
|
|
|
+ *
|
|
|
|
|
+ * @author ljd
|
|
|
|
|
+ */
|
|
|
|
|
+@Slf4j
|
|
|
|
|
+@Component
|
|
|
|
|
+public class DynamicThreadPoolManager {
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 线程池注册表
|
|
|
|
|
+ */
|
|
|
|
|
+ private final Map<String, ThreadPoolExecutor> threadPoolRegistry = new ConcurrentHashMap<>();
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 默认线程池名称
|
|
|
|
|
+ */
|
|
|
|
|
+ public static final String DEFAULT_POOL = "DEFAULT";
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 特征查询线程池名称
|
|
|
|
|
+ */
|
|
|
|
|
+ public static final String MULTI_GET_FEATURE_POOL = "MULTI_GET_FEATURE";
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Apollo 配置的线程池参数
|
|
|
|
|
+ * 配置格式:
|
|
|
|
|
+ * [
|
|
|
|
|
+ * {"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:[]}")
|
|
|
|
|
+ private List<DynamicThreadPoolConfig> threadPoolConfigs;
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 默认配置
|
|
|
|
|
+ */
|
|
|
|
|
+ private static final DynamicThreadPoolConfig DEFAULT_CONFIG;
|
|
|
|
|
+ private static final DynamicThreadPoolConfig MULTI_GET_FEATURE_CONFIG;
|
|
|
|
|
+
|
|
|
|
|
+ static {
|
|
|
|
|
+ // 针对 2 核 CPU 优化的默认配置
|
|
|
|
|
+ DEFAULT_CONFIG = new DynamicThreadPoolConfig();
|
|
|
|
|
+ DEFAULT_CONFIG.setPoolName(DEFAULT_POOL);
|
|
|
|
|
+ DEFAULT_CONFIG.setCorePoolSize(8);
|
|
|
|
|
+ DEFAULT_CONFIG.setMaxPoolSize(16);
|
|
|
|
|
+ DEFAULT_CONFIG.setQueueCapacity(1000);
|
|
|
|
|
+ DEFAULT_CONFIG.setKeepAliveSeconds(60);
|
|
|
|
|
+ DEFAULT_CONFIG.setRejectedPolicy("CALLER_RUNS");
|
|
|
|
|
+
|
|
|
|
|
+ // IO 密集型任务(Redis 查询),可以多一些线程
|
|
|
|
|
+ MULTI_GET_FEATURE_CONFIG = new DynamicThreadPoolConfig();
|
|
|
|
|
+ MULTI_GET_FEATURE_CONFIG.setPoolName(MULTI_GET_FEATURE_POOL);
|
|
|
|
|
+ MULTI_GET_FEATURE_CONFIG.setCorePoolSize(16);
|
|
|
|
|
+ MULTI_GET_FEATURE_CONFIG.setMaxPoolSize(32);
|
|
|
|
|
+ MULTI_GET_FEATURE_CONFIG.setQueueCapacity(1800);
|
|
|
|
|
+ MULTI_GET_FEATURE_CONFIG.setKeepAliveSeconds(60);
|
|
|
|
|
+ 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;
|
|
|
|
|
+
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private MeterRegistry meterRegistry;
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 定时监控调度器
|
|
|
|
|
+ */
|
|
|
|
|
+ private ScheduledExecutorService monitorScheduler;
|
|
|
|
|
+
|
|
|
|
|
+ @PostConstruct
|
|
|
|
|
+ public void init() {
|
|
|
|
|
+ // 初始化默认线程池
|
|
|
|
|
+ initThreadPool(DEFAULT_CONFIG);
|
|
|
|
|
+ initThreadPool(MULTI_GET_FEATURE_CONFIG);
|
|
|
|
|
+
|
|
|
|
|
+ // 根据 Apollo 配置覆盖
|
|
|
|
|
+ if (threadPoolConfigs != null && !threadPoolConfigs.isEmpty()) {
|
|
|
|
|
+ for (DynamicThreadPoolConfig config : threadPoolConfigs) {
|
|
|
|
|
+ updateThreadPool(config);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ 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 =====");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 初始化线程池
|
|
|
|
|
+ */
|
|
|
|
|
+ private void initThreadPool(DynamicThreadPoolConfig config) {
|
|
|
|
|
+ ThreadPoolExecutor executor = createThreadPoolExecutor(config);
|
|
|
|
|
+ threadPoolRegistry.put(config.getPoolName(), executor);
|
|
|
|
|
+ registerMetrics(executor, config.getPoolName());
|
|
|
|
|
+ log.info("Thread pool [{}] initialized: coreSize={}, maxSize={}, queueCapacity={}",
|
|
|
|
|
+ config.getPoolName(), config.getCorePoolSize(), config.getMaxPoolSize(), config.getQueueCapacity());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 注册线程池指标到 Micrometer
|
|
|
|
|
+ */
|
|
|
|
|
+ private void registerMetrics(ThreadPoolExecutor executor, String poolName) {
|
|
|
|
|
+ Gauge.builder("threadpool.core.size", executor, ThreadPoolExecutor::getCorePoolSize)
|
|
|
|
|
+ .tag("pool", poolName)
|
|
|
|
|
+ .description("Core pool size")
|
|
|
|
|
+ .register(meterRegistry);
|
|
|
|
|
+
|
|
|
|
|
+ Gauge.builder("threadpool.max.size", executor, ThreadPoolExecutor::getMaximumPoolSize)
|
|
|
|
|
+ .tag("pool", poolName)
|
|
|
|
|
+ .description("Maximum pool size")
|
|
|
|
|
+ .register(meterRegistry);
|
|
|
|
|
+
|
|
|
|
|
+ Gauge.builder("threadpool.active.count", executor, ThreadPoolExecutor::getActiveCount)
|
|
|
|
|
+ .tag("pool", poolName)
|
|
|
|
|
+ .description("Active thread count")
|
|
|
|
|
+ .register(meterRegistry);
|
|
|
|
|
+
|
|
|
|
|
+ Gauge.builder("threadpool.pool.size", executor, ThreadPoolExecutor::getPoolSize)
|
|
|
|
|
+ .tag("pool", poolName)
|
|
|
|
|
+ .description("Current pool size")
|
|
|
|
|
+ .register(meterRegistry);
|
|
|
|
|
+
|
|
|
|
|
+ Gauge.builder("threadpool.queue.size", executor, e -> e.getQueue().size())
|
|
|
|
|
+ .tag("pool", poolName)
|
|
|
|
|
+ .description("Queue size")
|
|
|
|
|
+ .register(meterRegistry);
|
|
|
|
|
+
|
|
|
|
|
+ Gauge.builder("threadpool.queue.capacity", () -> getQueueCapacity(poolName))
|
|
|
|
|
+ .tag("pool", poolName)
|
|
|
|
|
+ .description("Queue capacity")
|
|
|
|
|
+ .register(meterRegistry);
|
|
|
|
|
+
|
|
|
|
|
+ Gauge.builder("threadpool.completed.tasks", executor, ThreadPoolExecutor::getCompletedTaskCount)
|
|
|
|
|
+ .tag("pool", poolName)
|
|
|
|
|
+ .description("Completed task count")
|
|
|
|
|
+ .register(meterRegistry);
|
|
|
|
|
+
|
|
|
|
|
+ Gauge.builder("threadpool.task.count", executor, ThreadPoolExecutor::getTaskCount)
|
|
|
|
|
+ .tag("pool", poolName)
|
|
|
|
|
+ .description("Total task count")
|
|
|
|
|
+ .register(meterRegistry);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 创建线程池
|
|
|
|
|
+ */
|
|
|
|
|
+ private ThreadPoolExecutor createThreadPoolExecutor(DynamicThreadPoolConfig config) {
|
|
|
|
|
+ return new ThreadPoolExecutor(
|
|
|
|
|
+ config.getCorePoolSize(),
|
|
|
|
|
+ config.getMaxPoolSize(),
|
|
|
|
|
+ config.getKeepAliveSeconds(),
|
|
|
|
|
+ TimeUnit.SECONDS,
|
|
|
|
|
+ new ResizableLinkedBlockingQueue<>(config.getQueueCapacity()),
|
|
|
|
|
+ new ThreadFactoryBuilder().setNameFormat(config.getPoolName() + "-%d").build(),
|
|
|
|
|
+ getRejectedExecutionHandler(config.getRejectedPolicy())
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取拒绝策略(带告警包装)
|
|
|
|
|
+ */
|
|
|
|
|
+ private RejectedExecutionHandler getRejectedExecutionHandler(String policy) {
|
|
|
|
|
+ RejectedExecutionHandler originalHandler;
|
|
|
|
|
+ switch (policy.toUpperCase()) {
|
|
|
|
|
+ case "ABORT":
|
|
|
|
|
+ originalHandler = new ThreadPoolExecutor.AbortPolicy();
|
|
|
|
|
+ break;
|
|
|
|
|
+ case "DISCARD":
|
|
|
|
|
+ originalHandler = new ThreadPoolExecutor.DiscardPolicy();
|
|
|
|
|
+ break;
|
|
|
|
|
+ case "DISCARD_OLDEST":
|
|
|
|
|
+ originalHandler = new ThreadPoolExecutor.DiscardOldestPolicy();
|
|
|
|
|
+ break;
|
|
|
|
|
+ case "CALLER_RUNS":
|
|
|
|
|
+ default:
|
|
|
|
|
+ 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";
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 监听 Apollo 配置变更
|
|
|
|
|
+ * 注意:手动解析新配置值,避免与 @ApolloJsonValue 自动注入产生竞态条件
|
|
|
|
|
+ */
|
|
|
|
|
+ @ApolloConfigChangeListener
|
|
|
|
|
+ public void onConfigChange(ConfigChangeEvent changeEvent) {
|
|
|
|
|
+ if (changeEvent.isChanged("thread.pool.configs")) {
|
|
|
|
|
+ ConfigChange change = changeEvent.getChange("thread.pool.configs");
|
|
|
|
|
+ log.info("Thread pool config changed, old={}, new={}", change.getOldValue(), change.getNewValue());
|
|
|
|
|
+
|
|
|
|
|
+ // 手动解析新配置值,避免竞态条件
|
|
|
|
|
+ String newValue = change.getNewValue();
|
|
|
|
|
+ if (StringUtils.isNotBlank(newValue)) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ List<DynamicThreadPoolConfig> newConfigs = JSON.parseArray(newValue, DynamicThreadPoolConfig.class);
|
|
|
|
|
+ if (newConfigs != null) {
|
|
|
|
|
+ for (DynamicThreadPoolConfig config : newConfigs) {
|
|
|
|
|
+ updateThreadPool(config);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("Failed to parse thread pool config: {}", newValue, e);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 动态更新线程池参数
|
|
|
|
|
+ */
|
|
|
|
|
+ public void updateThreadPool(DynamicThreadPoolConfig config) {
|
|
|
|
|
+ // 参数验证
|
|
|
|
|
+ if (config.getCorePoolSize() <= 0 || config.getMaxPoolSize() <= 0) {
|
|
|
|
|
+ log.error("Invalid pool size for [{}]: corePoolSize={}, maxPoolSize={} must be positive",
|
|
|
|
|
+ config.getPoolName(), config.getCorePoolSize(), config.getMaxPoolSize());
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (config.getCorePoolSize() > config.getMaxPoolSize()) {
|
|
|
|
|
+ log.error("Invalid pool size for [{}]: corePoolSize={} > maxPoolSize={}",
|
|
|
|
|
+ config.getPoolName(), config.getCorePoolSize(), config.getMaxPoolSize());
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (config.getQueueCapacity() <= 0) {
|
|
|
|
|
+ log.error("Invalid queueCapacity for [{}]: {} must be positive",
|
|
|
|
|
+ config.getPoolName(), config.getQueueCapacity());
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ ThreadPoolExecutor executor = threadPoolRegistry.get(config.getPoolName());
|
|
|
|
|
+ if (executor == null) {
|
|
|
|
|
+ log.warn("Thread pool [{}] not found, creating new one", config.getPoolName());
|
|
|
|
|
+ initThreadPool(config);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 动态调整参数
|
|
|
|
|
+ int oldCoreSize = executor.getCorePoolSize();
|
|
|
|
|
+ int oldMaxSize = executor.getMaximumPoolSize();
|
|
|
|
|
+
|
|
|
|
|
+ // 注意:调整顺序很重要,避免 coreSize > maxSize 的情况
|
|
|
|
|
+ if (config.getCorePoolSize() > executor.getMaximumPoolSize()) {
|
|
|
|
|
+ executor.setMaximumPoolSize(config.getMaxPoolSize());
|
|
|
|
|
+ executor.setCorePoolSize(config.getCorePoolSize());
|
|
|
|
|
+ } else {
|
|
|
|
|
+ executor.setCorePoolSize(config.getCorePoolSize());
|
|
|
|
|
+ executor.setMaximumPoolSize(config.getMaxPoolSize());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ executor.setKeepAliveTime(config.getKeepAliveSeconds(), TimeUnit.SECONDS);
|
|
|
|
|
+
|
|
|
|
|
+ // 更新拒绝策略
|
|
|
|
|
+ RejectedExecutionHandler newHandler = getRejectedExecutionHandler(config.getRejectedPolicy());
|
|
|
|
|
+ executor.setRejectedExecutionHandler(newHandler);
|
|
|
|
|
+
|
|
|
|
|
+ // 更新队列容量(如果是可调整大小的队列)
|
|
|
|
|
+ if (executor.getQueue() instanceof ResizableLinkedBlockingQueue) {
|
|
|
|
|
+ ((ResizableLinkedBlockingQueue<?>) executor.getQueue()).setCapacity(config.getQueueCapacity());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ log.info("Thread pool [{}] updated: coreSize {} -> {}, maxSize {} -> {}, queueCapacity={}, rejectedPolicy={}",
|
|
|
|
|
+ config.getPoolName(), oldCoreSize, config.getCorePoolSize(),
|
|
|
|
|
+ oldMaxSize, config.getMaxPoolSize(), config.getQueueCapacity(), config.getRejectedPolicy());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取线程池
|
|
|
|
|
+ */
|
|
|
|
|
+ public ExecutorService getThreadPool(String poolName) {
|
|
|
|
|
+ ExecutorService executor = threadPoolRegistry.get(poolName);
|
|
|
|
|
+ if (executor == null) {
|
|
|
|
|
+ log.warn("Thread pool [{}] not found, using DEFAULT pool", poolName);
|
|
|
|
|
+ return threadPoolRegistry.get(DEFAULT_POOL);
|
|
|
|
|
+ }
|
|
|
|
|
+ return executor;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取默认线程池
|
|
|
|
|
+ */
|
|
|
|
|
+ public ExecutorService getDefaultPool() {
|
|
|
|
|
+ return getThreadPool(DEFAULT_POOL);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取特征查询线程池
|
|
|
|
|
+ */
|
|
|
|
|
+ public ExecutorService getMultiGetFeaturePool() {
|
|
|
|
|
+ return getThreadPool(MULTI_GET_FEATURE_POOL);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取线程池状态信息
|
|
|
|
|
+ */
|
|
|
|
|
+ public Map<String, ThreadPoolStats> getThreadPoolStats() {
|
|
|
|
|
+ Map<String, ThreadPoolStats> statsMap = new ConcurrentHashMap<>();
|
|
|
|
|
+ for (Map.Entry<String, ThreadPoolExecutor> entry : threadPoolRegistry.entrySet()) {
|
|
|
|
|
+ ThreadPoolExecutor executor = entry.getValue();
|
|
|
|
|
+ ThreadPoolStats stats = new ThreadPoolStats();
|
|
|
|
|
+ stats.setPoolName(entry.getKey());
|
|
|
|
|
+ stats.setCorePoolSize(executor.getCorePoolSize());
|
|
|
|
|
+ stats.setMaxPoolSize(executor.getMaximumPoolSize());
|
|
|
|
|
+ stats.setActiveCount(executor.getActiveCount());
|
|
|
|
|
+ stats.setPoolSize(executor.getPoolSize());
|
|
|
|
|
+ stats.setQueueSize(executor.getQueue().size());
|
|
|
|
|
+ stats.setQueueCapacity(getQueueCapacity(entry.getKey()));
|
|
|
|
|
+ stats.setCompletedTaskCount(executor.getCompletedTaskCount());
|
|
|
|
|
+ stats.setTaskCount(executor.getTaskCount());
|
|
|
|
|
+ statsMap.put(entry.getKey(), stats);
|
|
|
|
|
+ }
|
|
|
|
|
+ return statsMap;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取指定线程池的队列容量
|
|
|
|
|
+ */
|
|
|
|
|
+ private int getQueueCapacity(String poolName) {
|
|
|
|
|
+ if (threadPoolConfigs != null) {
|
|
|
|
|
+ for (DynamicThreadPoolConfig config : threadPoolConfigs) {
|
|
|
|
|
+ if (config.getPoolName().equals(poolName)) {
|
|
|
|
|
+ return config.getQueueCapacity();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // 返回默认值
|
|
|
|
|
+ if (DEFAULT_POOL.equals(poolName)) {
|
|
|
|
|
+ return DEFAULT_CONFIG.getQueueCapacity();
|
|
|
|
|
+ } else if (MULTI_GET_FEATURE_POOL.equals(poolName)) {
|
|
|
|
|
+ return MULTI_GET_FEATURE_CONFIG.getQueueCapacity();
|
|
|
|
|
+ }
|
|
|
|
|
+ return DEFAULT_CONFIG.getQueueCapacity();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @PreDestroy
|
|
|
|
|
+ public void shutdown() {
|
|
|
|
|
+ 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()) {
|
|
|
|
|
+ ThreadPoolExecutor executor = entry.getValue();
|
|
|
|
|
+ executor.shutdown();
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
|
|
|
|
|
+ executor.shutdownNow();
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (InterruptedException e) {
|
|
|
|
|
+ executor.shutdownNow();
|
|
|
|
|
+ Thread.currentThread().interrupt();
|
|
|
|
|
+ }
|
|
|
|
|
+ log.info("Thread pool [{}] shutdown completed", entry.getKey());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 线程池状态信息
|
|
|
|
|
+ */
|
|
|
|
|
+ @Data
|
|
|
|
|
+ public static class ThreadPoolStats {
|
|
|
|
|
+ private String poolName;
|
|
|
|
|
+ private int corePoolSize;
|
|
|
|
|
+ private int maxPoolSize;
|
|
|
|
|
+ private int activeCount;
|
|
|
|
|
+ private int poolSize;
|
|
|
|
|
+ private int queueSize;
|
|
|
|
|
+ private int queueCapacity;
|
|
|
|
|
+ private long completedTaskCount;
|
|
|
|
|
+ private long taskCount;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+}
|