背景

  在我们实际项目开发中,常常会为不同优先级的任务设置相对应的线程池。一般我们只关注相关池的相关参数如核心线程数据,最大线程数据等等参数,容易忽略了对线程池中实际运行情况的监控。

综上所述:线程池如果相当于黑盒一样在运行的话,对系统的不利的。本文提供了一种简单获取线程池运行状态的方式,可以将详情打印到日志或者对接到Prometheus上进行展示。

  有不少博主给出了动态修改线程的方式,但是由于生产环境是禁止,因此本文只提供了监控的功能。本代码应用项目架构为springboot。

代码类结构

ThreadPoolMonitor:线程池扩展类

ThreadPoolUtil:线程池工具类

ThreadPoolDetailInfo:bean类

ExecutorThreadPoolManager:线程池实现类

ThreadPoolController:线程池测试方法

线程池扩展类

  从类主要重写了ThreadPoolExecutor类中的shutdown、shutdownNow、beforeExecute和afterExecute,用于对每个任务进行执行前后的拦截,计算出每个任务的运行时间。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;
/**
* @ClassName ThreadPoolMonitor
* @authors kantlin
* @Date 2021/12/16 17:45
**/
public class ThreadPoolMonitor extends ThreadPoolExecutor {
private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolMonitor.class);
private final ConcurrentHashMap<String, Date> startTimes;
private final String poolName;
private long totalDiff; public ThreadPoolMonitor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, String poolName) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
this.startTimes = new ConcurrentHashMap();
this.poolName = poolName;
} @Override
public void shutdown() {
LOGGER.info("{} Going to shutdown. Executed tasks: {}, Running tasks: {}, Pending tasks: {}", new Object[]{this.poolName, this.getCompletedTaskCount(), this.getActiveCount(), this.getQueue().size()});
super.shutdown();
}
@Override
public List<Runnable> shutdownNow() {
LOGGER.info("{} Going to immediately shutdown. Executed tasks: {}, Running tasks: {}, Pending tasks: {}", new Object[]{this.poolName, this.getCompletedTaskCount(), this.getActiveCount(), this.getQueue().size()});
return super.shutdownNow();
} @Override
protected void beforeExecute(Thread t, Runnable r) {
this.startTimes.put(String.valueOf(r.hashCode()), new Date());
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
Date startDate = this.startTimes.remove(String.valueOf(r.hashCode()));
Date finishDate = new Date();
long diff = finishDate.getTime() - startDate.getTime();
this.totalDiff += diff;
} public long getTotalDiff() {
return this.totalDiff;
}
}

  线程工具类

import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit; /**
* @ClassName ThreadPoolUtil
* @authors kantlin
* @Date 2021/12/16 17:45
**/ @Component
public class ThreadPoolUtil {
private final HashMap<String, ThreadPoolMonitor> threadPoolExecutorHashMap = new HashMap(); public ThreadPoolUtil() {
} public ThreadPoolMonitor creatThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,String poolName) {
ThreadPoolMonitor threadPoolExecutor = new ThreadPoolMonitor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,threadFactory, poolName);
this.threadPoolExecutorHashMap.put(poolName, threadPoolExecutor);
return threadPoolExecutor;
} public HashMap<String, ThreadPoolMonitor> getThreadPoolExecutorHashMap() {
return this.threadPoolExecutorHashMap;
}

  创建线程bean类

import lombok.Data;

@Data
public class ThreadPoolDetailInfo {
//线程池名字
private String threadPoolName;
//当前线程池大小
private Integer poolSize;
//线程池核心线程数量
private Integer corePoolSize;
//线程池生命周期中最大线程数量
private Integer largestPoolSize;
//线程池中允许的最大线程数
private Integer maximumPoolSize;
//线程池完成的任务数目
private long completedTaskCount;
//线程池中当前活跃个数
private Integer active;
//线程池完成的任务个数
private long task;
//线程最大空闲时间
private long keepAliveTime;
//当前活跃线程的占比
private int activePercent;
//任务队列容量(阻塞队列)
private Integer queueCapacity;
//当前队列中任务的数量
private Integer queueSize;
//线程池中任务平均执行时长
private long avgExecuteTime; public ThreadPoolDetailInfo(String threadPoolName, Integer poolSize, Integer corePoolSize, Integer largestPoolSize, Integer maximumPoolSize, long completedTaskCount, Integer active, long task, long keepAliveTime, int activePercent, Integer queueCapacity, Integer queueSize, long avgExecuteTime) {
this.threadPoolName = threadPoolName;
this.poolSize = poolSize;
this.corePoolSize = corePoolSize;
this.largestPoolSize = largestPoolSize;
this.maximumPoolSize = maximumPoolSize;
this.completedTaskCount = completedTaskCount;
this.active = active;
this.task = task;
this.keepAliveTime = keepAliveTime;
this.activePercent = activePercent;
this.queueCapacity = queueCapacity;
this.queueSize = queueSize;
this.avgExecuteTime = avgExecuteTime;
}
}

线程池实现类

  在我的项目中,将线程池依次划分为high、normal、low、single四种线程池类型。不同优先级的任务将会被submit到不同的线程池中执行。

在业务中有判断线程池中queue的长度来决定是否投递任务,由于没有相应的拒绝策略,所以队列不设置长度。

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.*.newThread.ThreadPoolUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; @Component
public class ExecutorThreadPoolManager { @Autowired
private ThreadPoolUtil threadPoolUtil; @Value("${thread_pool_normal_level_thread_max_num}")
private Integer normalLevelThreadPoolThreadMaxNum;
@Value("${thread_pool_normal_level_core_thread_num}")
private Integer normalLevelThreadPoolCoreThreadNum;
@Value("${thread_pool_low_level_thread_max_num}")
private Integer lowLevelThreadPoolThreadMaxNum;
@Value("${thread_pool_low_level_core_thread_num}")
private Integer lowLevelThreadPoolCoreThreadNum; private ThreadPoolExecutor normalThreadPoolExecutor; private ThreadPoolExecutor highPriorityExecutor; private ThreadPoolExecutor lowPriorityExecutor; private ThreadPoolExecutor singleThreadPoolExecutor; @PostConstruct
public void initExecutor() {
ThreadFactory normalThreadFactory = new ThreadFactoryBuilder().setNameFormat("normal_task_thread_%d").build();
normalThreadPoolExecutor = threadPoolUtil.creatThreadPool(normalLevelThreadPoolCoreThreadNum, normalLevelThreadPoolThreadMaxNum, 0L,
TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), normalThreadFactory,"normal_level_thread_pool"); ThreadFactory highPriorityThreadFactory = new ThreadFactoryBuilder().setNameFormat("high_priority_level_task_thread_%d").build();
highPriorityExecutor = threadPoolUtil.creatThreadPool(normalLevelThreadPoolCoreThreadNum, normalLevelThreadPoolThreadMaxNum, 0L,
TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), highPriorityThreadFactory,"high_level_thread_pool"); ThreadFactory lowPriorityThreadFactory = new ThreadFactoryBuilder().setNameFormat("low_priority_level_task_thread_%d").build();
lowPriorityExecutor = threadPoolUtil.creatThreadPool(lowLevelThreadPoolCoreThreadNum, lowLevelThreadPoolThreadMaxNum, 0L,
TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), lowPriorityThreadFactory,"low_level_thread_pool"); ThreadFactory singleFactory = new ThreadFactoryBuilder().setNameFormat("single_task_thread_%d").build();
singleThreadPoolExecutor =threadPoolUtil.creatThreadPool(1, 1,
0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), singleFactory,"single_level_thread_pool");
} /**
* @author kantlin
* @date 2021/9/9
* @describe 定义三种线程池, 一般采集类的用低优, 正常业务的用中优, 用户手动请求API的用高优线程池
**/
public ThreadPoolExecutor getNormalThreadPoolExecutor() {
return normalThreadPoolExecutor;
} public ThreadPoolExecutor getHighPriorityExecutor() {
return highPriorityExecutor;
} public ThreadPoolExecutor getLowPriorityExecutor() {
return lowPriorityExecutor;
} public ThreadPoolExecutor getSingleThreadPoolExecutor() {
return singleThreadPoolExecutor;
} }

  创建线程池监控接口类

import com.alibaba.fastjson.JSONObject;
import com.*.newThread.ThreadPoolDetailInfo;
import com.*.newThread.ThreadPoolMonitor;
import com.*.newThread.ThreadPoolUtil;
import com.*.thread.ExecutorThreadPoolManager;
import io.swagger.annotations.Api;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.text.NumberFormat;
import java.util.*;
import java.util.concurrent.TimeUnit; /**
* @ClassName ThreadPoolController
* @authors kantlin
* @Date 2021/12/17 14:53
**/
@Api(description = "线程池监控接口")
@RestController
@RequestMapping(value = "api/threadpool")
public class ThreadPoolController {
private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolController.class); @Autowired
private ExecutorThreadPoolManager threadPool; @Autowired
private ThreadPoolUtil threadPoolUtil; @GetMapping(value = "/getThreadPools")
private List<String> getThreadPools() {
List<String> threadPools = new ArrayList();
if (!this.threadPoolUtil.getThreadPoolExecutorHashMap().isEmpty()) {
Iterator var2 = this.threadPoolUtil.getThreadPoolExecutorHashMap().entrySet().iterator(); while (var2.hasNext()) {
Map.Entry<String, ThreadPoolMonitor> entry = (Map.Entry) var2.next();
threadPools.add(entry.getKey());
}
} return threadPools;
} @GetMapping(value = "/getThreadPoolListInfo")
@Scheduled(cron = "${thread.poll.status.cron}")
private List<ThreadPoolDetailInfo> getThreadPoolListInfo() {
List<ThreadPoolDetailInfo> detailInfoList = new ArrayList();
if (!this.threadPoolUtil.getThreadPoolExecutorHashMap().isEmpty()) {
Iterator var2 = this.threadPoolUtil.getThreadPoolExecutorHashMap().entrySet().iterator();
while (var2.hasNext()) {
Map.Entry<String, ThreadPoolMonitor> entry = (Map.Entry) var2.next();
ThreadPoolDetailInfo threadPoolDetailInfo = this.threadPoolInfo(entry.getValue(), (String) entry.getKey());
detailInfoList.add(threadPoolDetailInfo);
}
}
LOGGER.info("Execute details of cuurent thread poll:{}", JSONObject.toJSONString(detailInfoList));
return detailInfoList;
} private ThreadPoolDetailInfo threadPoolInfo(ThreadPoolMonitor threadPool, String threadPoolName) {
BigDecimal activeCount = new BigDecimal(threadPool.getActiveCount());
BigDecimal maximumPoolSize = new BigDecimal(threadPool.getMaximumPoolSize());
BigDecimal result = activeCount.divide(maximumPoolSize, 2, 4);
NumberFormat numberFormat = NumberFormat.getPercentInstance();
numberFormat.setMaximumFractionDigits(2);
int queueCapacity = 0;
return new ThreadPoolDetailInfo(threadPoolName, threadPool.getPoolSize(), threadPool.getCorePoolSize(), threadPool.getLargestPoolSize(), threadPool.getMaximumPoolSize(), threadPool.getCompletedTaskCount(), threadPool.getActiveCount(), threadPool.getTaskCount(), threadPool.getKeepAliveTime(TimeUnit.MILLISECONDS), new Double(result.doubleValue() * 100).intValue(), queueCapacity, threadPool.getQueue().size(), threadPool.getTaskCount() == 0L ? 0L : threadPool.getTotalDiff() / threadPool.getTaskCount());
}
}

运行结果

  上面controller中的方法除了可以通过接口进行暴露外,还设置了定时任务定期的打印到日志中。方便对系统状态进行排查。

[
{
"active": 0,
"activePercent": 0,
"avgExecuteTime": 0,
"completedTaskCount": 0,
"corePoolSize": 20,
"keepAliveTime": 0,
"largestPoolSize": 0,
"maximumPoolSize": 20,
"poolSize": 0,
"queueCapacity": 0,
"queueSize": 0,
"task": 0,
"threadPoolName": "high_level_thread_pool"
},
{
"active": 0,
"activePercent": 0,
"avgExecuteTime": 0,
"completedTaskCount": 0,
"corePoolSize": 33,
"keepAliveTime": 0,
"largestPoolSize": 0,
"maximumPoolSize": 33,
"poolSize": 0,
"queueCapacity": 0,
"queueSize": 0,
"task": 0,
"threadPoolName": "low_level_thread_pool"
},
{
"active": 0,
"activePercent": 0,
"avgExecuteTime": 371,
"completedTaskCount": 14,
"corePoolSize": 20,
"keepAliveTime": 0,
"largestPoolSize": 14,
"maximumPoolSize": 20,
"poolSize": 14,
"queueCapacity": 0,
"queueSize": 0,
"task": 14,
"threadPoolName": "normal_level_thread_pool"
},
{
"active": 0,
"activePercent": 0,
"avgExecuteTime": 0,
"completedTaskCount": 0,
"corePoolSize": 1,
"keepAliveTime": 0,
"largestPoolSize": 0,
"maximumPoolSize": 1,
"poolSize": 0,
"queueCapacity": 0,
"queueSize": 0,
"task": 0,
"threadPoolName": "single_level_thread_pool"
}
]

Reference

Spring Boot线程池简单监控|转的更多相关文章

  1. Spring Boot 线程池

    参考 SpringBoot 线程池 程序猿DD-Spring Boot使用@Async实现异步调用:自定义线程池 如何优雅的使用和理解线程池 Spring Boot线程池的使用心得 博客园-Sprin ...

  2. spring boot: 线程池ThreadPoolTaskExecutor, 多线程

    由于项目里需要用到线程池来提高处理速度,记录一下spring的taskExecutor执行器来实现线程池. ThreadPoolTaskExecutor的配置在网上找了很多解释没找到,看了下Threa ...

  3. spring boot 线程池配置

    1.配置类 package cn.com.bonc.util; import java.util.concurrent.Executor; import java.util.concurrent.Th ...

  4. Spring Boot 线程池的使用和扩展 - 转载

    转载:http://blog.csdn.net/boling_cavalry/article/details/79120268 1.实战环境 windowns10: jdk1.8: springboo ...

  5. SpringCloud微服务实战——搭建企业级开发框架(四十四):【微服务监控告警实现方式一】使用Actuator + Spring Boot Admin实现简单的微服务监控告警系统

      业务系统正常运行的稳定性十分重要,作为SpringBoot的四大核心之一,Actuator让你时刻探知SpringBoot服务运行状态信息,是保障系统正常运行必不可少的组件.   spring-b ...

  6. spring @Async 线程池使用

    最近公司项目正逐渐从dubbo向springCloud转型,在本次新开发的需求中,全部使用springcloud进行,在使用时线程池,考虑使用spring封装的线程池,现将本次使用心得及内容记录下来 ...

  7. Spring Boot开启Druid数据库监控功能

    Druid是一个关系型数据库连接池,它是阿里巴巴的一个开源项目.Druid支持所有JDBC兼容的数据库,包括Oracle.MySQL.Derby.PostgreSQL.SQL Server.H2等.D ...

  8. 如何做自己的服务监控?spring boot 2.x服务监控揭秘

    Actuator是spring boot项目中非常强大一个功能,有助于对应用程序进行监视和管理,通过 restful api请求来监管.审计.收集应用的运行情况,针对微服务而言它是必不可少的一个环节. ...

  9. Spring集成线程池

    自己在程序中手动New很容易造成线程滥用,创建线程也是比较消耗资源的操作,所以建议如果有此需求,将线程池统一交给Spring框架进行管理. 如下: <!--Spring 集成线程池,不允许自己开 ...

  10. spring-boot-plus集成Spring Boot Admin管理和监控应用(十一)

    spring-boot-plus集成Spring Boot Admin管理和监控应用 spring boot admin Spring Boot Admin用来管理和监控Spring Boot应用程序 ...

随机推荐

  1. 「一」nginx介绍

    应用场景 静态资源(js.css.图片 ) 反向代理 缓存加速(动态资源),比如社区活跃度排名 负载均衡(动态扩容.容灾) API服务 一个请求先经过nginx,再到应用服务器,访问数据库/redis ...

  2. HarmonyOS Next 鸿蒙开发-如何使用服务端下发的RSA公钥(字符串)对明文数据进行加密

    如何使用服务端下发的RSA公钥(字符串)对明文数据进行加密 将服务器下发的RSA公钥字符串替换掉pubKeyStr即可实现,具体可参考如下代码: import { buffer, util } fro ...

  3. IvorySQL 4.0 之 Invisible Column 功能解析

    前言 随着数据库应用场景的多样化,用户对数据管理的灵活性和隐私性提出了更高要求.IvorySQL 作为一款基于 PostgreSQL 并兼容 Oracle 的开源数据库,始终致力于在功能上保持领先和创 ...

  4. 流式计算(四)-Flink Stream API 篇二

    个人原创文章,禁止任何形式转载,否则追究法律责任! 本文只发表在"公众号"和"博客园",其他均属复制粘贴!如果觉得排版不清晰,请查看公众号文章. 话说看图看核心 ...

  5. 支付宝当面付和微信付款码支付封装DLL

    项目中需要为客户对接支付宝的当面付和微信付款码支付.场景就是软件中生成金额订单,然后扫顾客的微信付款码或者支付宝的付款码完成收款.为此封装了此DLL,可以用在其他项目中,其他人也可以直接拿来用. 最主 ...

  6. Wolfram常用计算

    1.方程与方程组 例1:求解方程 参考表达式: solve x^3 + 2x^2 - 6x + 5 = 0 real 求解所有根 例2:求解方程: 参考表达式: solve ax^2+bx+c=0 注 ...

  7. 你了解 Java 的逃逸分析吗?

    Java 的逃逸分析 1. 定义 逃逸分析(Escape Analysis)是 JVM 的一种优化技术,用于分析对象的作用域,从而决定对象的分配方式或优化手段. 主要目的是判断一个对象是否会逃离当前方 ...

  8. app类型划分

    app类型分为native类型,web类型,hybrid类型 一.native类型 1.优点:直接依托于操作系统,交互性最强,性能最好,功能最为强大 2.缺点:开发成本高,无法跨平台,更新缓慢,审核周 ...

  9. 《PDPU: An Open-Source Posit Dot-Product Unit for Deep Learning Applications》(三)

    Supporting suitable alignment width: In several designs [8] [19], quire [33] format is adopted to re ...

  10. Sentinel源码—7.参数限流和注解的实现

    大纲 1.参数限流的原理和源码 2.@SentinelResource注解的使用和实现 1.参数限流的原理和源码 (1)参数限流规则ParamFlowRule的配置Demo (2)ParamFlowS ...