线程池监控
# 线程池监控
# 背景
系统时不时会出现短暂的响应慢,甚至偶尔超时,但监控面板上的CPU、内存、磁盘IO全都很正常。
排查发现: 订单服务的线程池悄悄"罢工"了 —— 核心线程全被占满,任务队列塞爆,新请求全部被拒绝,这种情况在常规监控中完全无法被发现!
解决方案: 引入线程池监控工具,支持动态伸缩,异常预警。
链路流程如下:
web请求->监控拦截器->线程池是否健康
- 良好 实时数据
- 警告 告警推送
- 危险 自动扩容
# 使用方式
# 1、引入坐标
<dependency>
<groupId>cn.lovecyy</groupId>
<artifactId>pool-monitor-starter</artifactId>
<version>${version}</version>
</dependency>
# 2、application.yml
relaxed:
thread-pool:
monitor:
#是否开启池监控
monitorEnabled: true
#预警阈值 超过则通知
alertThreshold: 80
alertChannels: email #通知方式 可自行选择处理
monitorIntervalMills: 10000 #监控频率 毫秒单位
adjustPoolNumEnabled: true #是否开启池自动伸缩
#若阈值低于20% 且收缩空闲间隔超过,则将池恢复原池大小
idleRatioMaxThreshold: 20 # 收缩阈值 低于20
idleRatioIntervalMills: 60000 #收缩空闲间隔 毫秒单位
adjustPoolMaxinumThreshold: 100 #最大扩展池大小
# 3、配置监控
增加 @ThreadPoolMonitor自定义注解,name不设置默认取bean名称
目前仅支持 ThreadPoolExecutor、ThreadPoolTaskExecutor线程池监控
@ThreadPoolMonitor(name = "测试线程池")
@Bean
public ThreadPoolExecutor testThreadPool() {
ThreadPoolExecutor orderProcessExecutor = new ThreadPoolExecutor(3, // 核心线程数
5, // 最大线程数
60, TimeUnit.SECONDS, // 空闲线程存活时间
new LinkedBlockingQueue<>(200), // 工作队列
new NamedThreadFactory("order-process-", false), new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
return orderProcessExecutor;
}
# 4、单元测试
正常使用线程池即可
for (int i = 0; i < 1000; i++) {
PoolOrder poolOrder = new PoolOrder();
poolOrder.setUsername("username" + i);
CompletableFuture.supplyAsync(() -> {
// 处理订单逻辑
return doProcessOrder(order);
}, testThreadPool);
}
# 扩展说明
# 1、获取池信息
private final ThreadPoolTaskMonitor monitor;
/**
* 获取当前池状态
*/
@GetMapping("/stats")
public List<ThreadPoolStats> getAllStats() {
return monitor.getAllPoolStats();
}
/**
* 获取周期内池平均趋势统计
*/
@GetMapping("/trend")
public Map<String, ThreadPoolTrend> getAllTrends() {
Map<String, ThreadPoolTrend> trends = new HashMap<>();
monitor.getAllPoolStats().forEach(stats -> {
trends.put(stats.getPoolName(), monitor.getTrend(stats.getPoolName()));
});
return trends;
}
# 2、自定义通知
实现AlertService注册成bean即可,默认的如下,不做任何处理
@Bean
public AlertService defaultAlertService() {
return (finalMsg, channels) -> {
//通知渠道根据yml配置的,多个,分割。
// log.info("线程池告警,通知渠道:{},消息:{}",channels,finalMsg);
};
}
# 3、监控面版指标
监控面板展示的关键指标:
| 指标名称 | 健康值 | 警告值 | 危险值 | 当前值 |
|---|---|---|---|---|
| 活跃线程数 | <70% | 70%-85% | >85% | 56% |
| 队列使用率 | <60% | 60%-80% | >80% | 23% |
| 任务完成率 | >95% | 85%-95% | <85% | 99.8% |
| 拒绝任务数 | 0 | <10 | ≥10 | 0 |
| 平均执行时间 | <300ms | 300ms-800ms | >800ms | 78ms |
# 4、线程池预热
通过监控分析,可以了解到不同时间段,线程池的紧张状态。
针对这种情况,我们可以进行峰值提前预热,将线程池扩大。
解决方案:
开发一个定时任务,进行线程池的自动伸缩。 可以合理的调整高峰、次高峰、低峰时候的合理线程数。
代码实现:
获取ThreadPoolTaskMonitor的bean对象,从里面根据名称查询到线程池,调用对应方法调整池参数;