本文设计部分参考知识星球小傅哥和 实现一个简易动态线程池 作者喔喔咿哈哈
本文监控部分参考小傅哥作业中半程客梦i
本文设计简易的动态线程池组件,可以直接嵌入到项目之中,采用Redis作为注册中心,Prometheus采集数据,Grafana进行动态数据显示。
本文代码在https://gitcode.com/tianshuowdnmd/dynamic-thread-pool
一 前言
1.1 什么是线程池
线程池(Thread Pool)是一种基于池化思想管理线程的工具,经常出现在多线程服务器中,如MySQL。
线程过多会带来额外的开销,其中包括创建销毁线程的开销、调度线程的开销等等,同时也降低了计算机的整体性能。线程池维护多个线程,等待监督管理者分配可并发执行的任务。这种做法,一方面避免了处理任务时创建销毁线程开销的代价,另一方面避免了线程数量膨胀导致的过分调度问题,保证了对内核的充分利用。
而本文描述线程池是JDK中提供的ThreadPoolExecutor类。
当然,使用线程池可以带来一系列好处:
- 降低资源消耗:通过池化技术重复利用已创建的线程,降低线程创建和销毁造成的损耗。
- 提高响应速度:任务到达时,无需等待线程创建即可立即执行。
- 提高线程的可管理性:线程是稀缺资源,如果无限制创建,不仅会消耗系统资源,还会因为线程的不合理分布导致资源调度失衡,降低系统的稳定性。使用线程池可以进行统一的分配、调优和监控。
- 提供更多更强大的功能:线程池具备可拓展性,允许开发人员向其中增加更多的功能。比如延时定时线程池ScheduledThreadPoolExecutor,就允许任务延期执行或定期执行。
ThreadPoolExecutor运行流程
1.2 任务调度机制
- 首先检测线程池运行状态,如果不是RUNNING,则直接拒绝,线程池要保证在RUNNING的状态下执行任务。
- 如果运行线程数 < corePoolSize,则创建并启动一个线程来执行新提交的任务。
- 如果运行线程数>= corePoolSize,且线程池内的阻塞队列未满,则将任务添加到该阻塞队列中。
- 如果运行线程数 >= corePoolSize && 运行线程数< maximumPoolSize,且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务。
- 如果运行线程数>= maximumPoolSize,并且线程池内的阻塞队列已满, 则根据拒绝策略来处理该任务, 默认的处理方式是直接抛异常。
1.3 为什么需要动态线程池
线程池在业务系统应该都有使用到,帮助业务流程提升效率以及管理线程,多数场景应用于大量的异步任务处理。虽然线程池提供了我们许多便利,但也并非尽善尽美,比如下面这些问题就无法很好解决。
-
动态线程池根据需求调整线程数,避免资源浪费或不足。
-
动态线程池支持运行时调整参数,适应配置变化。
-
线程池任务堆积,触发拒绝策略,影响既有业务正常运行。
1.4 线程池一般用来执行什么任务
二 后端整体设计
一些需要明确的点
自动装配配置层
包括
- Redis配置
- 线程池信息的读取
- Prometheus的配置
package cn.bugstack.middleware.dynamic.thread.pool.sdk.config;
import cn.bugstack.middleware.dynamic.thread.pool.sdk.trigger.listener.ThreadPoolConfigAdjustListener;
import cn.bugstack.middleware.dynamic.thread.pool.sdk.domain.DynamicThreadPoolService;
import cn.bugstack.middleware.dynamic.thread.pool.sdk.domain.IDynamicThreadPoolService;
import cn.bugstack.middleware.dynamic.thread.pool.sdk.domain.model.entity.ThreadPoolConfigEntity;
import cn.bugstack.middleware.dynamic.thread.pool.sdk.domain.model.valobj.RegistryEnumVO;
import cn.bugstack.middleware.dynamic.thread.pool.sdk.registry.IRegistry;
import cn.bugstack.middleware.dynamic.thread.pool.sdk.registry.redis.RedisRegistry;
import cn.bugstack.middleware.dynamic.thread.pool.sdk.trigger.job.ThreadPoolDataReportJob;
import org.apache.commons.lang.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RTopic;
import org.redisson.api.RedissonClient;
import org.redisson.codec.JsonJacksonCodec;
import org.redisson.config.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.PrometheusProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @author ts
* @description 动态配置入口
* @create 2025-1-6 15:37
*/
@Configuration
@ComponentScan(basePackages = "cn.bugstack.middleware.dynamic.thread.pool.sdk.trigger.controller")
@EnableConfigurationProperties(DynamicThreadPoolAutoProperties.class)
@EnableScheduling
public class DynamicThreadPoolAutoConfig {
private final Logger logger = LoggerFactory.getLogger(DynamicThreadPoolAutoConfig.class);
private String applicationName;
@Bean("dynamicThreadRedissonClient")
public RedissonClient redissonClient(DynamicThreadPoolAutoProperties properties) {
Config config = new Config();
// 根据需要可以设定编解码器;https://github.com/redisson/redisson/wiki/4.-%E6%95%B0%E6%8D%AE%E5%BA%8F%E5%88%97%E5%8C%96
config.setCodec(JsonJacksonCodec.INSTANCE);
config.useSingleServer()
.setAddress("redis://" + properties.getHost() + ":" + properties.getPort())
.setPassword(properties.getPassword())
.setConnectionPoolSize(properties.getPoolSize())
.setConnectionMinimumIdleSize(properties.getMinIdleSize())
.setIdleConnectionTimeout(properties.getIdleTimeout())
.setConnectTimeout(properties.getConnectTimeout())
.setRetryAttempts(properties.getRetryAttempts())
.setRetryInterval(properties.getRetryInterval())
.setPingConnectionInterval(properties.getPingInterval())
.setKeepAlive(properties.isKeepAlive())
;
RedissonClient redissonClient = Redisson.create(config);
logger.info("动态线程池,注册器(redis)链接初始化完成。{} {} {}", properties.getHost(), properties.getPoolSize(), !redissonClient.isShutdown());
return redissonClient;
}
@Bean
public IRegistry redisRegistry(RedissonClient dynamicThreadRedissonClient) {
return new RedisRegistry(dynamicThreadRedissonClient);
}
@Bean("dynamicThreadPollService")
public DynamicThreadPoolService dynamicThreadPollService(ApplicationContext applicationContext, Map<String, ThreadPoolExecutor> threadPoolExecutorMap, RedissonClient redissonClient) {
applicationName = applicationContext.getEnvironment().getProperty("spring.application.name");
if (StringUtils.isBlank(applicationName)) {
applicationName = "缺省的";
logger.warn("动态线程池,启动提示。SpringBoot 应用未配置 spring.application.name 无法获取到应用名称!");
}
// 获取缓存数据,设置本地线程池配置
Set<String> threadPoolKeys = threadPoolExecutorMap.keySet();
for (String threadPoolKey : threadPoolKeys) {
ThreadPoolConfigEntity threadPoolConfigEntity = redissonClient.<ThreadPoolConfigEntity>getBucket(RegistryEnumVO.THREAD_POOL_CONFIG_PARAMETER_LIST_KEY.getKey() + "_" + applicationName + "_" + threadPoolKey).get();
if (null == threadPoolConfigEntity) continue;
ThreadPoolExecutor threadPoolExecutor = threadPoolExecutorMap.get(threadPoolKey);
threadPoolExecutor.setCorePoolSize(threadPoolConfigEntity.getCorePoolSize());
threadPoolExecutor.setMaximumPoolSize(threadPoolConfigEntity.getMaximumPoolSize());
}
return new DynamicThreadPoolService(applicationName, threadPoolExecutorMap);
}
@Bean
public ThreadPoolDataReportJob threadPoolDataReportJob(IDynamicThreadPoolService dynamicThreadPoolService, IRegistry registry) {
return new ThreadPoolDataReportJob(dynamicThreadPoolService, registry);
}
@Bean
public ThreadPoolConfigAdjustListener threadPoolConfigAdjustListener(IDynamicThreadPoolService dynamicThreadPoolService, IRegistry registry) {
return new ThreadPoolConfigAdjustListener(dynamicThreadPoolService, registry);
}
@Bean(name = "dynamicThreadPoolRedisTopic")
public RTopic threadPoolConfigAdjustListener(RedissonClient redissonClient, ThreadPoolConfigAdjustListener threadPoolConfigAdjustListener) {
RTopic topic = redissonClient.getTopic(RegistryEnumVO.DYNAMIC_THREAD_POOL_REDIS_TOPIC.getKey() + "_" + applicationName);
topic.addListener(ThreadPoolConfigEntity.class, threadPoolConfigAdjustListener);
return topic;
}
// Prometheus配置
@Bean
public PrometheusConfigRunner prometheusConfigRunner(
ApplicationContext applicationContext,
WebEndpointProperties webEndpointProperties,
PrometheusProperties prometheusProperties
) {
webEndpointProperties.getExposure().setInclude(
new HashSet<>(Arrays.asList(
"health",
"prometheus"
))
);
prometheusProperties.setEnabled(true);
logger.info("动态线程池,Prometheus 配置初始化完成。");
return new PrometheusConfigRunner(
applicationContext
);
}
}
Prometheus配置类的具体实现
package cn.bugstack.middleware.dynamic.thread.pool.sdk.config;
import io.micrometer.core.instrument.ImmutableTag;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Tag;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.context.ApplicationContext;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ThreadPoolExecutor;
@Slf4j
@NoArgsConstructor
@AllArgsConstructor
public class PrometheusConfigRunner implements ApplicationRunner {
@Resource
private ApplicationContext applicationContext;
@Override
public void run(ApplicationArguments args) throws Exception {
String[] beanNamesForType = applicationContext.getBeanNamesForType(ThreadPoolExecutor.class);
for (String beanName : beanNamesForType) {
ThreadPoolExecutor executor = (ThreadPoolExecutor) applicationContext.getBean(beanName);
registerThreadPool(
applicationContext.getEnvironment().getProperty("spring.application.name"),
beanName,
executor
);
log.info("动态线程池,启动提示。SpringBoot 应用已启动动态线程池:{}", beanName);
}
}
private void registerThreadPool(
String applicationName,
String poolName,
ThreadPoolExecutor executor
) {
List<Tag> tags = Arrays.asList(
new ImmutableTag("applicationName", applicationName),
new ImmutableTag("poolName", poolName)
);
Metrics.gauge("thread_pool_core_size", tags, executor, ThreadPoolExecutor::getCorePoolSize);
Metrics.gauge("thread_pool_max_size", tags, executor, ThreadPoolExecutor::getMaximumPoolSize);
Metrics.gauge("thread_pool_active_thread_count", tags, executor, ThreadPoolExecutor::getActiveCount);
Metrics.gauge("thread_pool_size", tags, executor, ThreadPoolExecutor::getPoolSize);
Metrics.gauge("thread_pool_queue_size", tags, executor,
(threadPoolExecutor) -> threadPoolExecutor.getQueue().size()
);
Metrics.gauge("thread_pool_queue_remaining_capacity", tags, executor,
(threadPoolExecutor) -> threadPoolExecutor.getQueue().remainingCapacity()
);
log.info("Prometheus已经注册监控指标");
}
}
Redis注册中心
Redis的作用
-
使用Redis作为注册中心,上报线程池配置数据。
-
当有线程池数据修改时,通过发布订阅更新Redis中的数据。
-
前端获取的数据,直接从Redis中读取,返回给前端。
Redis中的方法
1 reportThreadPool
desc: 进行总线程池信息数据上报
@Override
public void reportThreadPool(List<ThreadPoolConfigEntity> threadPoolEntities) {
RList<ThreadPoolConfigEntity> list = redissonClient.getList(RegistryEnumVO.THREAD_POOL_CONFIG_LIST_KEY.getKey());
RLock lock = redissonClient.getLock(RegistryEnumVO.REPORT_THREAD_POOL_CONFIG_LIST_REDIS_LOCK_KEY.getKey());
String applicationName = threadPoolEntities.get(0).getAppName();
try {
boolean canHasLock = lock.tryLock(3000, 3000, TimeUnit.MILLISECONDS);
if (canHasLock) {
// 获取本应用线程池的开始索引
int index = -1;
for (int i = 0; i < list.size(); i++) {
ThreadPoolConfigEntity originalPoolConfig = list.get(i);
if (Objects.equals(originalPoolConfig.getAppName(), applicationName)) {
index = i;
break;
}
}
if (index == -1) {
// 如果不存在,直接添加到列表末尾
list.addAll(threadPoolEntities);
} else {
// 更新线程池配置
for (ThreadPoolConfigEntity newPoolConfig : threadPoolEntities) {
ThreadPoolConfigEntity originalPoolConfig = list.get(index);
if (!originalPoolConfig.equals(newPoolConfig)) {
list.fastRemove(index);
list.add(index, newPoolConfig);
}
index++;
}
}
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
2 reportThreadPoolConfigParameter
desc: 具体线程池信息的上传
@Override
public void reportThreadPoolConfigParameter(ThreadPoolConfigEntity threadPoolConfigEntity) {
String cacheKey = RegistryEnumVO.THREAD_POOL_CONFIG_PARAMETER_LIST_KEY.getKey() + "_" + threadPoolConfigEntity.getAppName() + "_" + threadPoolConfigEntity.getThreadPoolName();
RBucket<ThreadPoolConfigEntity> bucket = redissonClient.getBucket(cacheKey);
bucket.set(threadPoolConfigEntity, Duration.ofDays(30));
}
定时任务
每过X秒进行当前应用的线程池信息上报
public class ThreadPoolDataReportJob {
private final Logger logger = LoggerFactory.getLogger(ThreadPoolDataReportJob.class);
private final IDynamicThreadPoolService dynamicThreadPoolService;
private final IRegistry registry;
public ThreadPoolDataReportJob(IDynamicThreadPoolService dynamicThreadPoolService, IRegistry registry) {
this.dynamicThreadPoolService = dynamicThreadPoolService;
this.registry = registry;
}
@Scheduled(cron = "0/3 * * * * ?")
public void execReportThreadPoolList() {
List<ThreadPoolConfigEntity> threadPoolConfigEntities = dynamicThreadPoolService.queryThreadPoolList();
registry.reportThreadPool(threadPoolConfigEntities);
logger.info("动态线程池,上报线程池信息:{}", JSON.toJSONString(threadPoolConfigEntities));
for (ThreadPoolConfigEntity threadPoolConfigEntity : threadPoolConfigEntities) {
registry.reportThreadPoolConfigParameter(threadPoolConfigEntity);
logger.info("动态线程池,上报线程池配置:{}", JSON.toJSONString(threadPoolConfigEntity));
}
}
}
监听任务
该监听器绑定了topic
针对消息订阅发布功能,大部分使用的是kafka、RabbitMQ、ActiveMQ, RocketMQ等这几种,redis的订阅发布功能跟这三者相比,相对轻量,针对数据准确和安全性要求没有那么高可以直接使用
@Bean(name = "dynamicThreadPoolRedisTopic")
public RTopic threadPoolConfigAdjustListener(RedissonClient redissonClient, ThreadPoolConfigAdjustListener threadPoolConfigAdjustListener) {
RTopic topic = redissonClient.getTopic(RegistryEnumVO.DYNAMIC_THREAD_POOL_REDIS_TOPIC.getKey() + "_" + applicationName);
topic.addListener(ThreadPoolConfigEntity.class, threadPoolConfigAdjustListener);
return topic;
}
具体方法如下
desc:当有话题发布(线程池修改配置)的时候,进行更新修改后的数据进行上报
public class ThreadPoolConfigAdjustListener implements MessageListener<ThreadPoolConfigEntity> {
private Logger logger = LoggerFactory.getLogger(ThreadPoolConfigAdjustListener.class);
private final IDynamicThreadPoolService dynamicThreadPoolService;
private final IRegistry registry;
public ThreadPoolConfigAdjustListener(IDynamicThreadPoolService dynamicThreadPoolService, IRegistry registry) {
this.dynamicThreadPoolService = dynamicThreadPoolService;
this.registry = registry;
}
@Override
public void onMessage(CharSequence charSequence, ThreadPoolConfigEntity threadPoolConfigEntity) {
logger.info("动态线程池,调整线程池配置。线程池名称:{} 核心线程数:{} 最大线程数:{}", threadPoolConfigEntity.getThreadPoolName(), threadPoolConfigEntity.getPoolSize(), threadPoolConfigEntity.getMaximumPoolSize());
dynamicThreadPoolService.updateThreadPoolConfig(threadPoolConfigEntity);
// 更新后上报最新数据
List<ThreadPoolConfigEntity> threadPoolConfigEntities = dynamicThreadPoolService.queryThreadPoolList();
registry.reportThreadPool(threadPoolConfigEntities);
ThreadPoolConfigEntity threadPoolConfigEntityCurrent = dynamicThreadPoolService.queryThreadPoolConfigByName(threadPoolConfigEntity.getThreadPoolName());
registry.reportThreadPoolConfigParameter(threadPoolConfigEntityCurrent);
logger.info("动态线程池,上报线程池配置:{}", JSON.toJSONString(threadPoolConfigEntity));
}
}
控制层
该类之中有三个方法,
-
用于显示Redis注册中心的总配置信息和
-
单独的线程池配置信息
-
修改线程池配置信息
/**
* @author: ts
* @description
* @create: 2025/1/6 9:02
*/
@Slf4j
@RestController
@CrossOrigin("*")
@RequestMapping("dynamic/thread/pool/admin")
public class DynamicThreadPoolController {
@Resource
RedissonClient redissonClient;
@RequestMapping(value = "query_thread_pool_list",method = RequestMethod.GET)
public Response<List<ThreadPoolConfigEntity>> queryThreadPoolList(){
try {
RList<ThreadPoolConfigEntity> cacheList = redissonClient.getList("THREAD_POOL_CONFIG_LIST_KEY");
return Response.<List<ThreadPoolConfigEntity>>builder()
.code(Response.Code.SUCCESS.getCode())
.info(Response.Code.SUCCESS.getInfo())
.data(cacheList.readAll())
.build();
} catch (Exception e) {
return Response.<List<ThreadPoolConfigEntity>>builder()
.code(Response.Code.UN_ERROR.getCode())
.info(Response.Code.UN_ERROR.getInfo())
.build();
}
}
@RequestMapping(value = "query_thread_pool_config", method = RequestMethod.GET)
public Response<ThreadPoolConfigEntity> queryThreadPoolConfig(@RequestParam String appName, @RequestParam String threadPoolName) {
try {
String cacheKey = "THREAD_POOL_CONFIG_PARAMETER_LIST_KEY" + "_" + appName + "_" + threadPoolName;
ThreadPoolConfigEntity threadPoolConfigEntity = redissonClient.<ThreadPoolConfigEntity>getBucket(cacheKey).get();
return Response.<ThreadPoolConfigEntity>builder()
.code(Response.Code.SUCCESS.getCode())
.info(Response.Code.SUCCESS.getInfo())
.data(threadPoolConfigEntity)
.build();
} catch (Exception e) {
log.error("查询线程池配置异常", e);
return Response.<ThreadPoolConfigEntity>builder()
.code(Response.Code.UN_ERROR.getCode())
.info(Response.Code.UN_ERROR.getInfo())
.build();
}
}
@RequestMapping(value = "update_thread_pool_config", method = RequestMethod.POST)
public Response<Boolean> updateThreadPoolConfig(@RequestBody ThreadPoolConfigEntity request) {
try {
log.info("修改线程池配置开始 {} {} {}", request.getAppName(), request.getThreadPoolName(), JSON.toJSONString(request));
RTopic topic = redissonClient.getTopic("DYNAMIC_THREAD_POOL_REDIS_TOPIC" + "_" + request.getAppName());
topic.publish(request);
log.info("修改线程池配置完成 {} {}", request.getAppName(), request.getThreadPoolName());
return Response.<Boolean>builder()
.code(Response.Code.SUCCESS.getCode())
.info(Response.Code.SUCCESS.getInfo())
.data(true)
.build();
} catch (Exception e) {
log.error("修改线程池配置异常 {}", JSON.toJSONString(request), e);
return Response.<Boolean>builder()
.code(Response.Code.UN_ERROR.getCode())
.info(Response.Code.UN_ERROR.getInfo())
.data(false)
.build();
}
}
}
线程池服务层
实现三个方法对应控制层
/**
* @author: ts
* @description
* @create: 2025/1/6 9:02
*/
public class DynamicThreadPoolService implements IDynamicThreadPoolService {
private final Logger logger = LoggerFactory.getLogger(DynamicThreadPoolService.class);
private final String applicationName;
private final Map<String, ThreadPoolExecutor> threadPoolExecutorMap;
public DynamicThreadPoolService(String applicationName, Map<String, ThreadPoolExecutor> threadPoolExecutorMap) {
this.applicationName = applicationName;
this.threadPoolExecutorMap = threadPoolExecutorMap;
}
@Override
public List<ThreadPoolConfigEntity> queryThreadPoolList() {
Set<String> threadPoolBeanNames = threadPoolExecutorMap.keySet();
List<ThreadPoolConfigEntity> threadPoolVOS = new ArrayList<>(threadPoolBeanNames.size());
for (String beanName : threadPoolBeanNames) {
ThreadPoolExecutor threadPoolExecutor = threadPoolExecutorMap.get(beanName);
ThreadPoolConfigEntity threadPoolConfigVO = new ThreadPoolConfigEntity(applicationName, beanName);
threadPoolConfigVO.setCorePoolSize(threadPoolExecutor.getCorePoolSize());
threadPoolConfigVO.setMaximumPoolSize(threadPoolExecutor.getMaximumPoolSize());
threadPoolConfigVO.setActiveCount(threadPoolExecutor.getActiveCount());
threadPoolConfigVO.setPoolSize(threadPoolExecutor.getPoolSize());
threadPoolConfigVO.setQueueType(threadPoolExecutor.getQueue().getClass().getSimpleName());
threadPoolConfigVO.setQueueSize(threadPoolExecutor.getQueue().size());
threadPoolConfigVO.setRemainingCapacity(threadPoolExecutor.getQueue().remainingCapacity());
threadPoolVOS.add(threadPoolConfigVO);
}
return threadPoolVOS;
}
@Override
public ThreadPoolConfigEntity queryThreadPoolConfigByName(String threadPoolName) {
ThreadPoolExecutor threadPoolExecutor = threadPoolExecutorMap.get(threadPoolName);
if (null == threadPoolExecutor) return new ThreadPoolConfigEntity(applicationName, threadPoolName);
// 线程池配置数据
ThreadPoolConfigEntity threadPoolConfigVO = new ThreadPoolConfigEntity(applicationName, threadPoolName);
threadPoolConfigVO.setCorePoolSize(threadPoolExecutor.getCorePoolSize());
threadPoolConfigVO.setMaximumPoolSize(threadPoolExecutor.getMaximumPoolSize());
threadPoolConfigVO.setActiveCount(threadPoolExecutor.getActiveCount());
threadPoolConfigVO.setPoolSize(threadPoolExecutor.getPoolSize());
threadPoolConfigVO.setQueueType(threadPoolExecutor.getQueue().getClass().getSimpleName());
threadPoolConfigVO.setQueueSize(threadPoolExecutor.getQueue().size());
threadPoolConfigVO.setRemainingCapacity(threadPoolExecutor.getQueue().remainingCapacity());
if (logger.isDebugEnabled()) {
logger.info("动态线程池,配置查询 应用名:{} 线程名:{} 池化配置:{}", applicationName, threadPoolName, JSON.toJSONString(threadPoolConfigVO));
}
return threadPoolConfigVO;
}
@Override
public void updateThreadPoolConfig(ThreadPoolConfigEntity threadPoolConfigEntity) {
if (null == threadPoolConfigEntity || !applicationName.equals(threadPoolConfigEntity.getAppName())) return;
ThreadPoolExecutor threadPoolExecutor = threadPoolExecutorMap.get(threadPoolConfigEntity.getThreadPoolName());
if (null == threadPoolExecutor) return;
// 设置参数 「调整核心线程数和最大线程数」
threadPoolExecutor.setCorePoolSize(threadPoolConfigEntity.getCorePoolSize());
threadPoolExecutor.setMaximumPoolSize(threadPoolConfigEntity.getMaximumPoolSize());
}
}
POM
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.26.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
<version>1.9.0</version>
</dependency>
</dependencies>
三 总体流程
其中,由于在业务模块定义了线程池的 Bean,这个 Bean 是 ThreadPoolExecutor 类型的。当 Spring 启动时,它会创建这个Bean,并将其添加到内部的Bean容器中。
其中有个 Map<String, ThreadPoolExecutor> threadPoolExecutorMap集合。这个 Map 是由 Spring 自动注入的,它包含了所有类型为 ThreadPoolExecutor 的 Bean。键是 Bean 的名称,值是对应的 Bean 实例。因此,这个 Map 中会包含业务模块中定义的线程池。
当应用启动的时候,动态线程池组件会自动将这些Bean进行管理,将其进行Redis注册,通过管理端进行发布订阅定时管理。
四 前端设计
本人前端啥也不会,纯询问AI进行的代码编写
采用React的框架的Next.js进行静态资源打包,将打包好的out的文件全部拿出来,全部复制放进resource的static文件夹中,就可以直接同构端口进行访问了。
修改界面
监控界面
Redis中数据