当前位置: 首页 > news >正文

不改代码,不重启,我把线上线程池的核心数从 10 改成了 100

图片

你的团队是否在为线程池参数的设置而烦恼?

  • • 大促前: “流量要暴增了,赶紧把所有核心应用的线程池参数调大三倍,发版!”

  • • 大促后: “流量降下来了,为了节省资源,再把参数调回去,又得发一次版...”

  • • 日常运维: “这个服务的线程池好像有点小,队列都满了,但改配置重启影响太大,先忍忍吧...”

线程池的静态配置,让我们的应用变得僵化,无法灵活应对流量的变化。理想的系统应该具备在运行时动态调整线程池参数的能力。

本文将带你从 0 到 1,构建一个基于动态配置中心、对业务代码零侵入的动态线程池 Starter。只需引入依赖并在配置中心动动手指,就能实时调整线上应用的线程池配置,无需重启。

1. 项目设计与核心思路

我们的 dynamic-threadpool-starter 目标如下:

  1. 1. 动态调整: 允许在运行时,通过外部配置中心(以 Nacos 为例)修改 ThreadPoolTaskExecutor 的核心参数,如 corePoolSizemaxPoolSizequeueCapacity

  2. 2. 自动发现: 自动发现 Spring 容器中所有需要被动态管理的线程池 Bean。

  3. 3. 实时监控: (可选增强)自动将动态线程池的核心指标(如活跃线程数、队列大小)暴露给 Micrometer,以便在 Prometheus/Grafana 中监控。

  4. 4. 无侵入: 业务开发者只需像平常一样定义和使用 ThreadPoolTaskExecutor Bean,无需关心动态调整的逻辑。

核心实现机制:动态配置中心 + 配置监听器

  1. 1. 配置中心: 我们将线程池的配置参数存储在 Nacos 中。

  2. 2. 监听器: Starter 的核心是一个 Nacos 的配置监听器 (Listener)。

  3. 3. 运行时变更: 当 Nacos 中的配置发生变更时,监听器会被触发。它会解析新的配置,并从 Spring 的 ApplicationContext 中找到对应名称的 ThreadPoolTaskExecutor Bean,然后调用其 setCorePoolSize() 等方法,将新的参数实时应用上去。

2. 创建 Starter 项目与核心组件

我们采用 autoconfigure + starter 的双模块结构。

步骤 2.1: 依赖 (autoconfigure 模块)
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency>
</dependencies>
步骤 2.2: 实现核心服务 (ThreadPoolDynamicAdjuster)

这是整个 Starter 的技术核心,负责监听配置并动态调整 Bean。

package com.example.threadpool.autoconfigure;import com.alibaba.nacos.api.config.ConfigService;
import com.alibaba.nacos.api.config.listener.Listener;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;public class ThreadPoolDynamicAdjuster {private static final Logger log = LoggerFactory.getLogger(ThreadPoolDynamicAdjuster.class);@Autowiredprivate ConfigService configService; // Nacos Config Service@Autowiredprivate ConfigurableApplicationContext applicationContext;@Autowiredprivate DynamicThreadPoolProperties properties;private final ObjectMapper objectMapper = new ObjectMapper();@PostConstructpublic void init() throws Exception {// 项目启动时,注册一个监听器configService.addListener(properties.getDataId(), properties.getGroup(), new Listener() {@Overridepublic Executor getExecutor() {return null; // 使用默认}@Overridepublic void receiveConfigInfo(String configInfo) {log.info("接收到线程池配置变更:\n{}", configInfo);try {// 解析收到的 JSON/YAML 配置Map<String, ThreadPoolProperties> poolConfigs = parseConfig(configInfo);poolConfigs.forEach((beanName, props) -> {updateThreadPool(beanName, props);});} catch (Exception e) {log.error("动态更新线程池配置失败", e);}}});}private void updateThreadPool(String beanName, ThreadPoolProperties props) {try {ThreadPoolTaskExecutor executor = applicationContext.getBean(beanName, ThreadPoolTaskExecutor.class);log.info("更新线程池 '{}' -> coreSize: {}, maxSize: {}", beanName, props.getCorePoolSize(), props.getMaxPoolSize());executor.setCorePoolSize(props.getCorePoolSize());executor.setMaximumPoolSize(props.getMaxPoolSize());// 注意:队列容量 (queueCapacity) 通常不能在运行时动态修改,因为它涉及到队列的重新创建。// 但核心和最大线程数是可以的。} catch (Exception e) {log.error("找不到或更新线程池Bean '{}' 失败", beanName, e);}}// ... parseConfig and ThreadPoolProperties model class ...private static class ThreadPoolProperties {private int corePoolSize;private int maxPoolSize;// Getters and Setters...}
}

3. 自动装配的魔法 (DynamicThreadPoolAutoConfiguration)

步骤 3.1: 配置属性类
@ConfigurationProperties(prefix = "dynamic.thread-pool")
public class DynamicThreadPoolProperties {private boolean enabled = false;private String dataId = "dynamic-threadpool.json";private String group = "DEFAULT_GROUP";// Getters and Setters...
}
步骤 3.2: 自动配置主类
@Configuration
@EnableConfigurationProperties(DynamicThreadPoolProperties.class)
@ConditionalOnProperty(prefix = "dynamic.thread-pool", name = "enabled", havingValue = "true")
@ConditionalOnClass(ConfigService.class) // 依赖 Nacos
public class DynamicThreadPoolAutoConfiguration {@Beanpublic ThreadPoolDynamicAdjuster threadPoolDynamicAdjuster() {return new ThreadPoolDynamicAdjuster();}
}
步骤 3.3: 注册自动配置

在 autoconfigure 模块的 resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中添加:

com.example.threadpool.autoconfigure.DynamicThreadPoolAutoConfiguration

4. 如何使用我们的 Starter

步骤 4.1: 引入依赖
在业务项目中引入我们的 Starter 和 Nacos Config Starter。

<dependency><groupId>com.example</groupId><artifactId>dynamic-threadpool-spring-boot-starter</artifactId><version>1.0.0</version>
</dependency>
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

步骤 4.2: 在业务应用中定义线程池 Bean
业务开发者像往常一样定义他们需要的线程池。关键是 Bean 的名称要清晰、唯一。

@Configuration
public class MyAsyncConfig {@Bean("myOrderExecutor") // <-- Bean 名称将用于动态配置public ThreadPoolTaskExecutor myOrderExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();executor.setCorePoolSize(5);executor.setMaxPoolSize(10);executor.setQueueCapacity(100);executor.setThreadNamePrefix("OrderAsync-");executor.initialize();return executor;}
}

步骤 4.3: 配置 bootstrap.yml 和 application.yml

# bootstrap.yml - 用于引导 Nacos 连接
spring:cloud:nacos:config:server-addr: 127.0.0.1:8848# application.yml - 启用我们的 Starter
dynamic:thread-pool:enabled: truedata-id: "myapp-threadpool-config" # Nacos中的配置IDgroup: "PROD"

步骤 4.4: 在 Nacos 中配置
在 Nacos 控制台,创建一个 Data ID 为 myapp-threadpool-config,Group 为 PROD 的配置,内容为 JSON 格式:

{"myOrderExecutor": {"corePoolSize": 10,"maxPoolSize": 20},"anotherExecutorBeanName": {"corePoolSize": 5,"maxPoolSize": 5}
}

Key (myOrderExecutor) 必须与 Spring 中定义的 Bean 名称完全一致。

验证:

  1. 1. 启动应用,myOrderExecutor 的核心线程数是 5 (代码中定义的初始值)。

  2. 2. 在 Nacos 控制台,将 corePoolSize 修改为 15 并发布。

  3. 3. 稍等片刻,观察应用日志,你会看到“接收到线程池配置变更”和“更新线程池 'myOrderExecutor'”的日志。

  4. 4. 此时,线程池的 corePoolSize 已经在不重启应用的情况下,被动态修改为了 15

总结

通过自定义一个 Spring Boot Starter,我们利用动态配置中心和 Spring Bean 的可变性,成功地将一个静态的、固化在代码中的线程池,变成了一个可实时观测、动态调整的“弹性”资源。


文章转载自:

http://hbz105Mk.wfdLz.cn
http://gOVQLIDL.wfdLz.cn
http://GNMvXL0g.wfdLz.cn
http://aKyhslUZ.wfdLz.cn
http://94cBgByV.wfdLz.cn
http://c2Z5qThe.wfdLz.cn
http://8F4fpzdY.wfdLz.cn
http://0dEK7j5q.wfdLz.cn
http://UztWOR7m.wfdLz.cn
http://yH2lOTFY.wfdLz.cn
http://MEi32mBj.wfdLz.cn
http://L14gMpzB.wfdLz.cn
http://PRAGG1XM.wfdLz.cn
http://sag0zOFg.wfdLz.cn
http://hJrtsZlU.wfdLz.cn
http://7uRrsExB.wfdLz.cn
http://2yPXantS.wfdLz.cn
http://Bgkqushc.wfdLz.cn
http://5UAwxHLT.wfdLz.cn
http://hqiLOGVJ.wfdLz.cn
http://ksMdLJX3.wfdLz.cn
http://cis1caZz.wfdLz.cn
http://NE6AduyX.wfdLz.cn
http://EWShw3VJ.wfdLz.cn
http://CJiwDsMr.wfdLz.cn
http://iP5sHruU.wfdLz.cn
http://145RBgMV.wfdLz.cn
http://8DuZbFxV.wfdLz.cn
http://mn6qgHYB.wfdLz.cn
http://P0HNuTdv.wfdLz.cn
http://www.dtcms.com/a/369401.html

相关文章:

  • 红黑树 + 双链表最小调度器原型
  • MySQL InnoDB 的 MVCC 机制
  • CRYPT32!CryptMsgUpdate函数分析两次CRYPT32!PkiAsn1Decode的作用
  • 智能健康新纪元:第一视角计算如何重塑科学减肥认知
  • Linux常见命令总结 合集二:基本命令、目录操作命令、文件操作命令、压缩文件操作、查找命令、权限命令、其他命令
  • FairGuard游戏加固产品常见问题解答
  • 2025年外贸服装软件TOP3推荐榜单,高效管理必备选择
  • 为什么说 Linode 和 DigitalOcean 的差距,不止于 VPS?
  • 十大常用算法(待更新)
  • c#动态树形表达式详解
  • 字符串格式化——`vsnprintf`函数
  • 【Flutter】drag_select_grid_view: ^0.6.2 使用
  • Android的DTBO详解
  • C++小数精度、四舍五入的疑惑
  • 操作系统——同步与互斥
  • 2025年跨领域管理能力提升认证路径分析
  • 常用的轻代码软件哪个好?
  • 双轴倾角传感器厂家与物联网角度传感器应用全解析
  • 【开题答辩全过程】以 高校教室管理系统为例,包含答辩的问题和答案
  • 科普:指令回调地址与数据回调地址
  • CSP-J初赛for(auto)用法
  • 谙流 ASK 技术解析(一):秒级扩容
  • 阿里云ESA 没有数据发送到SLS的解决
  • 【Python】根据开始时间、结束时间计算中间时间
  • 《Istio故障溯源:从流量劫持异常到服务网格的底层博弈》
  • STC携手VEX发起全球首个碳资产RWA生态,泰国峰会即将引爆绿色金融
  • 工业设备管理软件与AI_HawkEye智能运维平台_璞华大数据
  • 调试寄录之dc-dc芯片
  • 显存与内存
  • nVisual从入门到精通—基础知识