項目結構
threadpool-monitor/
├── pom.xml
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── zhangxueliang/
│ │ │ └── monitor/
│ │ │ ├── ThreadPoolMonitorStarter.java
│ │ │ ├── annotation/
│ │ │ │ ├── EnableThreadPoolMonitor.java
│ │ │ │ └── MonitorThreadPool.java
│ │ │ ├── config/
│ │ │ │ ├── MonitorProperties.java
│ │ │ │ └── ThreadPoolAutoConfig.java
│ │ │ ├── core/
│ │ │ │ ├── MonitorableThreadPoolExecutor.java
│ │ │ │ ├── ThreadPoolMonitor.java
│ │ │ │ └── ThreadPoolRegistry.java
│ │ │ ├── endpoint/
│ │ │ │ ├── ThreadPoolEndpoint.java
│ │ │ │ └── ThreadPoolEndpointConfig.java
│ │ │ ├── metrics/
│ │ │ │ └── ThreadPoolMetrics.java
│ │ │ ├── web/
│ │ │ │ ├── ThreadPoolMonitorController.java
│ │ │ │ └── ThreadPoolStatistics.java
│ │ │ └── advice/
│ │ │ └── ThreadPoolAdvice.java
│ │ └── resources/
│ │ ├── META-INF/
│ │ │ └── spring.factories
│ │ └── static/
│ │ └── monitor.html
│ └── test/
└── README.md
1. Maven配置文件 (pom.xml)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>zhangxueliang</groupId>
<artifactId>threadpool-monitor</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<name>ThreadPool Monitor</name>
<description>Spring Boot Thread Pool Monitoring Starter</description>
<properties>
<java.version>1.8</java.version>
<spring-boot.version>2.0.6.RELEASE</spring-boot.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Starter AOP -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- Spring Boot Actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- JSON Processing -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- 測試依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<classifier>exec</classifier>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</project>
2. 啓動器類
package zhangxueliang.monitor;
import org.springframework.context.annotation.Import;
import zhangxueliang.monitor.config.ThreadPoolAutoConfig;
import java.lang.annotation.*;
/**
* 啓用線程池監控註解
* 在Spring Boot應用上添加此註解即可啓用線程池監控
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ThreadPoolAutoConfig.class)
public @interface EnableThreadPoolMonitor {
}
3. 自動配置類
package zhangxueliang.monitor.config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import zhangxueliang.monitor.ThreadPoolMonitorStarter;
import zhangxueliang.monitor.core.ThreadPoolMonitor;
import zhangxueliang.monitor.core.ThreadPoolRegistry;
/**
* 線程池自動配置類
*/
@Configuration
@EnableConfigurationProperties(MonitorProperties.class)
@ConditionalOnProperty(prefix = "threadpool.monitor", name = "enabled", havingValue = "true", matchIfMissing = true)
public class ThreadPoolAutoConfig {
@Bean
@ConditionalOnMissingBean
public ThreadPoolRegistry threadPoolRegistry() {
return new ThreadPoolRegistry();
}
@Bean
@ConditionalOnMissingBean
public ThreadPoolMonitor threadPoolMonitor(ThreadPoolRegistry registry,
MonitorProperties properties) {
return new ThreadPoolMonitor(registry, properties);
}
@Bean
public ThreadPoolMonitorStarter threadPoolMonitorStarter(ThreadPoolRegistry registry,
MonitorProperties properties) {
return new ThreadPoolMonitorStarter(registry, properties);
}
}
4. 配置屬性類
package zhangxueliang.monitor.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 監控配置屬性
*/
@Data
@ConfigurationProperties(prefix = "threadpool.monitor")
public class MonitorProperties {
/**
* 是否啓用監控
*/
private boolean enabled = true;
/**
* 監控端點路徑
*/
private String endpointPath = "/threadpool/monitor";
/**
* 是否啓用Web控制枱
*/
private boolean webEnabled = true;
/**
* Web控制枱路徑
*/
private String webPath = "/threadpool/console";
/**
* 是否啓用Actuator端點
*/
private boolean actuatorEnabled = true;
/**
* Actuator端點ID
*/
private String actuatorId = "threadpool";
/**
* 監控數據刷新間隔(毫秒)
*/
private int refreshInterval = 5000;
/**
* 告警配置
*/
private AlertConfig alert = new AlertConfig();
@Data
public static class AlertConfig {
/**
* 隊列使用率告警閾值(0-100)
*/
private int queueUsageThreshold = 80;
/**
* 活躍線程告警閾值(0-100)
*/
private int activeThreadThreshold = 80;
/**
* 任務拒絕告警閾值
*/
private int rejectionThreshold = 10;
/**
* 任務失敗率告警閾值(0-100)
*/
private int failureRateThreshold = 10;
/**
* 是否啓用郵件告警
*/
private boolean emailAlert = false;
/**
* 是否啓用日誌告警
*/
private boolean logAlert = true;
}
}
5. 啓動器實現類
package zhangxueliang.monitor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import zhangxueliang.monitor.config.MonitorProperties;
import zhangxueliang.monitor.core.ThreadPoolRegistry;
/**
* 線程池監控啓動器
*/
@Component
@Slf4j
public class ThreadPoolMonitorStarter implements CommandLineRunner {
private final ThreadPoolRegistry registry;
private final MonitorProperties properties;
@Autowired
public ThreadPoolMonitorStarter(ThreadPoolRegistry registry,
MonitorProperties properties) {
this.registry = registry;
this.properties = properties;
}
@Override
public void run(String... args) {
log.info("線程池監控組件已啓動");
log.info("監控端點: {}", properties.getEndpointPath());
log.info("Web控制枱: {}", properties.getWebPath());
log.info("刷新間隔: {}ms", properties.getRefreshInterval());
if (properties.isActuatorEnabled()) {
log.info("Actuator端點已啓用: /actuator/{}", properties.getActuatorId());
}
}
}
6. 監控註解
package zhangxueliang.monitor.annotation;
import org.springframework.context.annotation.Import;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import java.lang.annotation.*;
import java.util.concurrent.Executor;
/**
* 監控線程池註解
* 可以標註在方法或類上,自動創建並監控線程池
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MonitorThreadPool {
/**
* 線程池名稱
*/
String value() default "";
/**
* 核心線程數
*/
int corePoolSize() default 5;
/**
* 最大線程數
*/
int maximumPoolSize() default 10;
/**
* 隊列容量
*/
int queueCapacity() default Integer.MAX_VALUE;
/**
* 線程名稱前綴
*/
String threadNamePrefix() default "monitored-thread";
/**
* 是否允許核心線程超時
*/
boolean allowCoreThreadTimeOut() default false;
/**
* 線程空閒時間(秒)
*/
long keepAliveSeconds() default 60;
}
7. 線程池註冊表
package zhangxueliang.monitor.core;
import zhangxueliang.monitor.metrics.ThreadPoolMetrics;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 線程池註冊中心
*/
@Component
public class ThreadPoolRegistry {
private final Map<String, ThreadPoolExecutor> registry = new ConcurrentHashMap<>();
/**
* 註冊線程池
*/
public void register(String poolName, ThreadPoolExecutor executor) {
registry.put(poolName, executor);
}
/**
* 獲取線程池
*/
public ThreadPoolExecutor getThreadPool(String poolName) {
return registry.get(poolName);
}
/**
* 獲取所有線程池
*/
public Map<String, ThreadPoolExecutor> getAllThreadPools() {
return new ConcurrentHashMap<>(registry);
}
/**
* 獲取所有線程池的監控指標
*/
public Map<String, ThreadPoolMetrics> getAllMetrics() {
Map<String, ThreadPoolMetrics> metrics = new ConcurrentHashMap<>();
registry.forEach((name, executor) -> {
metrics.put(name, ThreadPoolMetrics.fromExecutor(name, executor));
});
return metrics;
}
/**
* 獲取指定線程池的監控指標
*/
public ThreadPoolMetrics getMetrics(String poolName) {
ThreadPoolExecutor executor = registry.get(poolName);
return executor != null ? ThreadPoolMetrics.fromExecutor(poolName, executor) : null;
}
/**
* 移除線程池
*/
public void remove(String poolName) {
registry.remove(poolName);
}
/**
* 獲取線程池數量
*/
public int size() {
return registry.size();
}
}
8. 可監控的線程池執行器
package zhangxueliang.monitor.core;
import lombok.extern.slf4j.Slf4j;
import zhangxueliang.monitor.metrics.ThreadPoolMetrics;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLong;
/**
* 可監控的線程池執行器
*/
@Slf4j
public class MonitorableThreadPoolExecutor extends ThreadPoolExecutor {
// 監控指標
private final AtomicLong submittedTaskCount = new AtomicLong(0);
private final AtomicLong completedTaskCount = new AtomicLong(0);
private final AtomicLong failedTaskCount = new AtomicLong(0);
private final AtomicLong totalExecutionTime = new AtomicLong(0);
private final AtomicLong rejectedTaskCount = new AtomicLong(0);
// 線程池名稱
private final String poolName;
public MonitorableThreadPoolExecutor(String poolName,
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, new MonitorableRejectedExecutionHandler(handler));
this.poolName = poolName;
((MonitorableRejectedExecutionHandler) getRejectedExecutionHandler())
.setThreadPool(this);
}
@Override
public void execute(Runnable command) {
submittedTaskCount.incrementAndGet();
long startTime = System.currentTimeMillis();
super.execute(() -> {
try {
command.run();
completedTaskCount.incrementAndGet();
} catch (Exception e) {
failedTaskCount.incrementAndGet();
log.error("Thread pool [{}] task execution failed", poolName, e);
} finally {
long endTime = System.currentTimeMillis();
totalExecutionTime.addAndGet(endTime - startTime);
}
});
}
@Override
public <T> Future<T> submit(Callable<T> task) {
submittedTaskCount.incrementAndGet();
long startTime = System.currentTimeMillis();
Future<T> future = super.submit(() -> {
try {
T result = task.call();
completedTaskCount.incrementAndGet();
return result;
} catch (Exception e) {
failedTaskCount.incrementAndGet();
throw e;
} finally {
long endTime = System.currentTimeMillis();
totalExecutionTime.addAndGet(endTime - startTime);
}
});
return future;
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (t != null) {
failedTaskCount.incrementAndGet();
}
}
/**
* 獲取線程池監控指標
*/
public ThreadPoolMetrics getMetrics() {
long submitted = submittedTaskCount.get();
long completed = completedTaskCount.get();
long failed = failedTaskCount.get();
long avgExecutionTime = completed > 0 ? totalExecutionTime.get() / completed : 0;
return ThreadPoolMetrics.builder()
.poolName(poolName)
.corePoolSize(getCorePoolSize())
.maximumPoolSize(getMaximumPoolSize())
.poolSize(getPoolSize())
.activeCount(getActiveCount())
.largestPoolSize(getLargestPoolSize())
.completedTaskCount(getCompletedTaskCount())
.taskCount(getTaskCount())
.queueSize(getQueue().size())
.queueRemainingCapacity(getQueue().remainingCapacity())
.submittedTasks(submitted)
.completedTasks(completed)
.failedTasks(failed)
.averageExecutionTime(avgExecutionTime)
.rejectionCount(rejectedTaskCount.get())
.isShutdown(isShutdown())
.isTerminated(isTerminated())
.build();
}
/**
* 增加拒絕任務計數
*/
public void incrementRejectedCount() {
rejectedTaskCount.incrementAndGet();
}
/**
* 可監控的拒絕執行處理器
*/
private static class MonitorableRejectedExecutionHandler implements RejectedExecutionHandler {
private final RejectedExecutionHandler delegate;
private MonitorableThreadPoolExecutor threadPool;
public MonitorableRejectedExecutionHandler(RejectedExecutionHandler delegate) {
this.delegate = delegate;
}
public void setThreadPool(MonitorableThreadPoolExecutor threadPool) {
this.threadPool = threadPool;
}
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
if (threadPool != null) {
threadPool.incrementRejectedCount();
}
log.warn("Thread pool task rejected, pool: {}, active threads: {}, queue size: {}",
threadPool != null ? threadPool.poolName : "unknown",
executor.getActiveCount(),
executor.getQueue().size());
delegate.rejectedExecution(r, executor);
}
}
}
9. 監控指標類
package zhangxueliang.monitor.metrics;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 線程池監控指標
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ThreadPoolMetrics {
// 基礎信息
private String poolName;
private Date timestamp;
// 線程池配置
private int corePoolSize;
private int maximumPoolSize;
// 運行狀態
private int poolSize;
private int activeCount;
private int largestPoolSize;
// 任務統計
private long taskCount;
private long completedTaskCount;
private long submittedTasks;
private long completedTasks;
private long failedTasks;
// 隊列信息
private int queueSize;
private int queueRemainingCapacity;
// 性能指標
private long averageExecutionTime; // 平均執行時間(ms)
private long rejectionCount; // 拒絕次數
// 狀態
private boolean isShutdown;
private boolean isTerminated;
/**
* 從ThreadPoolExecutor創建監控指標
*/
public static ThreadPoolMetrics fromExecutor(String poolName, ThreadPoolExecutor executor) {
if (executor instanceof MonitorableThreadPoolExecutor) {
return ((MonitorableThreadPoolExecutor) executor).getMetrics();
}
return ThreadPoolMetrics.builder()
.poolName(poolName)
.timestamp(new Date())
.corePoolSize(executor.getCorePoolSize())
.maximumPoolSize(executor.getMaximumPoolSize())
.poolSize(executor.getPoolSize())
.activeCount(executor.getActiveCount())
.largestPoolSize(executor.getLargestPoolSize())
.completedTaskCount(executor.getCompletedTaskCount())
.taskCount(executor.getTaskCount())
.queueSize(executor.getQueue().size())
.queueRemainingCapacity(executor.getQueue().remainingCapacity())
.isShutdown(executor.isShutdown())
.isTerminated(executor.isTerminated())
.build();
}
// 計算指標
public double getUtilization() {
return maximumPoolSize > 0 ? (double) activeCount / maximumPoolSize : 0;
}
public double getQueueUtilization() {
int capacity = queueSize + queueRemainingCapacity;
return capacity > 0 ? (double) queueSize / capacity : 0;
}
public double getSuccessRate() {
return submittedTasks > 0 ? (double) completedTasks / submittedTasks : 1.0;
}
public double getFailureRate() {
return submittedTasks > 0 ? (double) failedTasks / submittedTasks : 0;
}
}
10. 監控控制器
package zhangxueliang.monitor.web;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.web.bind.annotation.*;
import zhangxueliang.monitor.core.ThreadPoolRegistry;
import zhangxueliang.monitor.metrics.ThreadPoolMetrics;
import java.util.Map;
/**
* 線程池監控Web接口
*/
@RestController
@RequestMapping("${threadpool.monitor.endpoint-path:/threadpool/monitor}")
@ConditionalOnProperty(prefix = "threadpool.monitor", name = "webEnabled", havingValue = "true", matchIfMissing = true)
public class ThreadPoolMonitorController {
@Autowired
private ThreadPoolRegistry registry;
/**
* 獲取所有線程池的監控數據
*/
@GetMapping("/metrics")
public Map<String, ThreadPoolMetrics> getAllMetrics() {
return registry.getAllMetrics();
}
/**
* 獲取指定線程池的監控數據
*/
@GetMapping("/metrics/{poolName}")
public ThreadPoolMetrics getMetrics(@PathVariable String poolName) {
return registry.getMetrics(poolName);
}
/**
* 獲取線程池監控摘要
*/
@GetMapping("/summary")
public ThreadPoolStatistics getSummary() {
Map<String, ThreadPoolMetrics> allMetrics = registry.getAllMetrics();
ThreadPoolStatistics statistics = new ThreadPoolStatistics();
statistics.setPoolCount(allMetrics.size());
long totalActiveThreads = 0;
long totalQueueSize = 0;
long totalRejections = 0;
long totalFailedTasks = 0;
long totalCompletedTasks = 0;
for (ThreadPoolMetrics metrics : allMetrics.values()) {
totalActiveThreads += metrics.getActiveCount();
totalQueueSize += metrics.getQueueSize();
totalRejections += metrics.getRejectionCount();
totalFailedTasks += metrics.getFailedTasks();
totalCompletedTasks += metrics.getCompletedTasks();
}
statistics.setTotalActiveThreads(totalActiveThreads);
statistics.setTotalQueueSize(totalQueueSize);
statistics.setTotalRejections(totalRejections);
statistics.setTotalFailedTasks(totalFailedTasks);
statistics.setTotalCompletedTasks(totalCompletedTasks);
return statistics;
}
/**
* 獲取線程池列表
*/
@GetMapping("/pools")
public Map<String, String> getPoolList() {
Map<String, ThreadPoolMetrics> allMetrics = registry.getAllMetrics();
Map<String, String> poolList = new java.util.HashMap<>();
allMetrics.forEach((name, metrics) -> {
poolList.put(name, String.format("Active: %d, Queue: %d, Rejections: %d",
metrics.getActiveCount(), metrics.getQueueSize(), metrics.getRejectionCount()));
});
return poolList;
}
}
11. 監控統計類
package zhangxueliang.monitor.web;
import lombok.Data;
/**
* 線程池監控統計信息
*/
@Data
public class ThreadPoolStatistics {
private int poolCount;
private long totalActiveThreads;
private long totalQueueSize;
private long totalRejections;
private long totalFailedTasks;
private long totalCompletedTasks;
private long timestamp = System.currentTimeMillis();
}
12. Actuator端點配置
package zhangxueliang.monitor.endpoint;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import zhangxueliang.monitor.core.ThreadPoolRegistry;
import zhangxueliang.monitor.metrics.ThreadPoolMetrics;
import java.util.Map;
/**
* Actuator監控端點
*/
@Component
@Endpoint(id = "${threadpool.monitor.actuator-id:threadpool}")
@ConditionalOnProperty(prefix = "threadpool.monitor", name = "actuatorEnabled", havingValue = "true", matchIfMissing = true)
public class ThreadPoolEndpoint {
private final ThreadPoolRegistry registry;
public ThreadPoolEndpoint(ThreadPoolRegistry registry) {
this.registry = registry;
}
@ReadOperation
public Map<String, ThreadPoolMetrics> metrics() {
return registry.getAllMetrics();
}
@ReadOperation
public Map<String, Object> info() {
Map<String, ThreadPoolMetrics> metrics = registry.getAllMetrics();
Map<String, Object> info = new java.util.HashMap<>();
info.put("poolCount", metrics.size());
long totalActive = metrics.values().stream()
.mapToLong(ThreadPoolMetrics::getActiveCount)
.sum();
long totalQueue = metrics.values().stream()
.mapToLong(ThreadPoolMetrics::getQueueSize)
.sum();
long totalRejections = metrics.values().stream()
.mapToLong(ThreadPoolMetrics::getRejectionCount)
.sum();
info.put("totalActiveThreads", totalActive);
info.put("totalQueueSize", totalQueue);
info.put("totalRejections", totalRejections);
return info;
}
}
13. AOP切面 - 自動創建監控線程池
package zhangxueliang.monitor.advice;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import zhangxueliang.monitor.annotation.MonitorThreadPool;
import zhangxueliang.monitor.core.MonitorableThreadPoolExecutor;
import zhangxueliang.monitor.core.ThreadPoolRegistry;
import java.lang.reflect.Field;
import java.util.concurrent.*;
/**
* 監控線程池切面
*/
@Aspect
@Component
@Slf4j
public class ThreadPoolAdvice {
@Autowired
private ThreadPoolRegistry registry;
/**
* 攔截@MonitorThreadPool註解的方法
*/
@Around("@annotation(monitorThreadPool)")
public Object monitorThreadPool(ProceedingJoinPoint joinPoint,
MonitorThreadPool monitorThreadPool) throws Throwable {
// 獲取目標對象
Object target = joinPoint.getTarget();
// 查找並創建線程池
String poolName = monitorThreadPool.value();
if (poolName.isEmpty()) {
poolName = target.getClass().getSimpleName() + "-ThreadPool";
}
// 檢查是否已存在線程池
ThreadPoolExecutor executor = registry.getThreadPool(poolName);
if (executor == null) {
executor = createMonitorableThreadPool(poolName, monitorThreadPool);
registry.register(poolName, executor);
log.info("Created monitored thread pool: {}", poolName);
}
// 執行原方法
return joinPoint.proceed();
}
/**
* 創建可監控的線程池
*/
private MonitorableThreadPoolExecutor createMonitorableThreadPool(
String poolName, MonitorThreadPool config) {
BlockingQueue<Runnable> workQueue;
if (config.queueCapacity() == Integer.MAX_VALUE) {
workQueue = new LinkedBlockingQueue<>();
} else {
workQueue = new ArrayBlockingQueue<>(config.queueCapacity());
}
ThreadFactory threadFactory = new ThreadFactory() {
private final AtomicInteger threadNumber = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName(config.threadNamePrefix() + "-" + threadNumber.getAndIncrement());
thread.setDaemon(false);
return thread;
}
};
MonitorableThreadPoolExecutor executor = new MonitorableThreadPoolExecutor(
poolName,
config.corePoolSize(),
config.maximumPoolSize(),
config.keepAliveSeconds(),
TimeUnit.SECONDS,
workQueue,
threadFactory,
new ThreadPoolExecutor.AbortPolicy()
);
executor.allowCoreThreadTimeOut(config.allowCoreThreadTimeOut());
return executor;
}
}
14. Spring自動配置註冊
# src/main/resources/META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
zhangxueliang.monitor.config.ThreadPoolAutoConfig
15. 簡單的Web監控界面
<!-- src/main/resources/static/monitor.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>線程池監控控制枱</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: Arial, sans-serif; background: #f5f5f5; padding: 20px; }
.container { max-width: 1200px; margin: 0 auto; }
.header { background: #fff; padding: 20px; border-radius: 8px; margin-bottom: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.header h1 { color: #333; margin-bottom: 10px; }
.stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 15px; margin-bottom: 20px; }
.stat-card { background: #fff; padding: 15px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.stat-card h3 { color: #666; font-size: 14px; margin-bottom: 10px; }
.stat-card .value { font-size: 24px; font-weight: bold; color: #1890ff; }
.pools { background: #fff; border-radius: 8px; padding: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.pools h2 { margin-bottom: 20px; color: #333; }
.pool-table { width: 100%; border-collapse: collapse; }
.pool-table th, .pool-table td { padding: 12px 15px; text-align: left; border-bottom: 1px solid #eee; }
.pool-table th { background: #fafafa; font-weight: bold; color: #666; }
.pool-table tr:hover { background: #f9f9f9; }
.status-good { color: #52c41a; }
.status-warning { color: #faad14; }
.status-danger { color: #f5222d; }
.refresh-btn { background: #1890ff; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; }
.refresh-btn:hover { background: #40a9ff; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🚀 線程池監控控制枱</h1>
<p>實時監控應用程序中的線程池運行狀態</p>
<button class="refresh-btn" onclick="loadData()">刷新數據</button>
</div>
<div class="stats" id="stats"></div>
<div class="pools">
<h2>線程池詳情</h2>
<table class="pool-table" id="poolTable">
<thead>
<tr>
<th>線程池名稱</th>
<th>核心線程數</th>
<th>最大線程數</th>
<th>活躍線程</th>
<th>隊列大小</th>
<th>任務總數</th>
<th>拒絕次數</th>
<th>狀態</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
<script>
// 加載監控數據
async function loadData() {
try {
// 加載統計數據
const summaryRes = await fetch('/threadpool/monitor/summary');
const summary = await summaryRes.json();
// 更新統計卡片
document.getElementById('stats').innerHTML = `
<div class="stat-card">
<h3>線程池總數</h3>
<div class="value">${summary.poolCount}</div>
</div>
<div class="stat-card">
<h3>活躍線程數</h3>
<div class="value">${summary.totalActiveThreads}</div>
</div>
<div class="stat-card">
<h3>隊列任務數</h3>
<div class="value">${summary.totalQueueSize}</div>
</div>
<div class="stat-card">
<h3>拒絕任務數</h3>
<div class="value">${summary.totalRejections}</div>
</div>
`;
// 加載線程池詳情
const metricsRes = await fetch('/threadpool/monitor/metrics');
const metrics = await metricsRes.json();
// 更新表格
const tbody = document.querySelector('#poolTable tbody');
tbody.innerHTML = '';
for (const [name, data] of Object.entries(metrics)) {
const utilization = data.activeCount / data.maximumPoolSize;
let statusClass = 'status-good';
if (utilization > 0.8) statusClass = 'status-warning';
if (utilization > 0.95) statusClass = 'status-danger';
const row = document.createElement('tr');
row.innerHTML = `
<td><strong>${name}</strong></td>
<td>${data.corePoolSize}</td>
<td>${data.maximumPoolSize}</td>
<td class="${statusClass}">${data.activeCount}</td>
<td>${data.queueSize}/${data.queueSize + data.queueRemainingCapacity}</td>
<td>${data.taskCount}</td>
<td>${data.rejectionCount}</td>
<td class="${statusClass}">${data.isShutdown ? '已關閉' : '運行中'}</td>
`;
tbody.appendChild(row);
}
} catch (error) {
console.error('加載監控數據失敗:', error);
alert('加載監控數據失敗,請檢查服務是否正常');
}
}
// 頁面加載時自動加載數據
document.addEventListener('DOMContentLoaded', loadData);
// 每5秒自動刷新
setInterval(loadData, 5000);
</script>
</body>
</html>
16. 使用説明文檔
創建 README.md 文件:
# ThreadPool Monitor
Spring Boot 線程池監控組件
## 功能特性
- ✅ 自動監控線程池運行狀態
- ✅ 提供RESTful API監控接口
- ✅ 集成Spring Boot Actuator
- ✅ 提供Web監控控制枱
- ✅ 支持線程池創建註解
- ✅ 實時收集監控指標
- ✅ 支持告警閾值配置
## 快速開始
### 1. 添加依賴
在Spring Boot項目中添加依賴:
```xml
<dependency>
<groupId>zhangxueliang</groupId>
<artifactId>threadpool-monitor</artifactId>
<version>1.0.0</version>
</dependency>
2. 啓用監控
在Spring Boot啓動類上添加註解:
@SpringBootApplication
@EnableThreadPoolMonitor
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
3. 配置參數(可選)
threadpool:
monitor:
enabled: true
endpoint-path: /threadpool/monitor
web-path: /threadpool/console
actuator-enabled: true
refresh-interval: 5000
alert:
queue-usage-threshold: 80
active-thread-threshold: 80
rejection-threshold: 10
4. 使用監控註解
@Service
public class OrderService {
@MonitorThreadPool(
value = "order-process-pool",
corePoolSize = 5,
maximumPoolSize = 10,
queueCapacity = 100,
threadNamePrefix = "order-thread"
)
public void processOrder(Order order) {
// 業務邏輯
}
}
監控接口
REST API
GET /threadpool/monitor/metrics- 獲取所有線程池指標GET /threadpool/monitor/metrics/{poolName}- 獲取指定線程池指標GET /threadpool/monitor/summary- 獲取監控摘要GET /threadpool/monitor/pools- 獲取線程池列表
Actuator端點
GET /actuator/threadpool- 獲取監控數據GET /actuator/threadpool/info- 獲取監控信息
Web控制枱
訪問 http://localhost:8080/threadpool/console 查看監控界面
監控指標
- 線程池配置:核心線程數、最大線程數
- 運行狀態:活躍線程數、池大小、隊列大小
- 任務統計:提交任務數、完成任務數、失敗任務數
- 性能指標:平均執行時間、拒絕次數
- 狀態信息:是否關閉、是否終止
構建部署
# 打包
mvn clean package
# 安裝到本地倉庫
mvn clean install
# 部署到私有倉庫
mvn clean deploy
注意事項
- 確保Spring Boot版本為2.0.6.RELEASE
- 默認監控端口為8080,可通過server.port修改
- 監控數據每5秒刷新一次
- 線程池創建後會自動註冊到監控中心
## 17. 打包和發佈
### 打包JAR:
```bash
mvn clean package
安裝到本地倉庫:
mvn clean install
在其他Spring Boot項目中使用:
- 添加依賴:
<dependency>
<groupId>zhangxueliang</groupId>
<artifactId>threadpool-monitor</artifactId>
<version>1.0.0</version>
</dependency>
- 啓用監控:
@SpringBootApplication
@EnableThreadPoolMonitor
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
- 訪問監控:
- Web控制枱:
http://localhost:8080/threadpool/console - API接口:
http://localhost:8080/threadpool/monitor/metrics - Actuator端點:
http://localhost:8080/actuator/threadpool
特點總結
- 開箱即用:只需添加註解即可啓用完整監控功能
- 零代碼侵入:通過註解和AOP實現,不影響業務代碼
- 全面監控:覆蓋線程池所有關鍵指標
- 多接口支持:提供REST API、Actuator端點、Web控制枱
- 易於集成:與Spring Boot 2.0.6完美兼容
- 配置靈活:支持豐富的配置選項
- 獨立JAR:不依賴外部組件,打包即用