Spring Boot 自定义异步线程池
异步任务处理是提高应用性能和响应速度的关键技术之一。Spring Boot 提供了 ThreadPoolTaskExecutor 作为线程池的实现,帮助开发者轻松管理和执行异步任务. 在项目中,我们发现有人在配置类中加上 @EnableAsync , 然后 application.yml 没有进行相关线程池的配置,这是一件非常危险的事,因为默认 ThreadPoolTaskExecutor 采用的是 缓冲队列的容量,默认为 INT 的最大值,许的最大线程数,默认为 INT 的最大值(2 的 31 次方-1),要是任务比较耗时,同时任务数量多的话就会造成内存溢出。
具体可以参考 TaskExecutionAutoConfiguration
配置类
application.yml 配置类有以下熟悉可以配置
yml
spring.task.execution.pool.core-size=2
spring.task.execution.pool.max-size=5
spring.task.execution.pool.queue-capacity=10
spring.task.execution.pool.keep-alive=60s
spring.task.execution.pool.allow-core-thread-timeout=true
spring.task.execution.shutdown.await-termination=false
spring.task.execution.shutdown.await-termination-period=
spring.task.execution.thread-name-prefix=task-
具体含义如下:
- spring.task.execution.pool.core-size:线程池创建时的初始化线程数,默认为 8
- spring.task.execution.pool.max-size:线程池的最大线程数,默认为 int 最大值
- spring.task.execution.pool.queue-capacity:用来缓冲执行任务的队列,默认为 int 最大值
- spring.task.execution.pool.keep-alive:线程终止前允许保持空闲的时间
- spring.task.execution.pool.allow-core-thread-timeout:是否允许核心线程超时
- spring.task.execution.shutdown.await-termination:是否等待剩余任务完成后才关闭应用
- spring.task.execution.shutdown.await-termination-period:等待剩余任务完成的最大时间
- spring.task.execution.thread-name-prefix:线程名的前缀
最佳实践
为了更好的进行线程池的管控,我们一般会自定义线程池,其次线程池隔离, 比如不同之间的线程池进行相互隔离, 下面介绍如何在 spring boot 中自定义线程池 starter 组件,这里只是把 starter 核心的代码放出来, 在企业组件中,我们还做了额外的工作,包括监控,动态线程池等。后续会开一个企业级 starter 组件合集,到时会详细介绍这部分内容
定义属性对象
java
@Data
@ConfigurationProperties(prefix = "async.config")
public class AsyncThreadPoolProperties {
private int corePoolSize = Runtime.getRuntime().availableProcessors();
private int maxPoolSize = Runtime.getRuntime().availableProcessors();
private int keepAliveTime = 60;
private int queueCapacity = 10000;
private String threadPrefixName = "custom-thread-pool-";
}
定义配置类
java
@Configuration
@Slf4j
@EnableConfigurationProperties(value = {AsyncThreadPoolProperties.class})
public class ThreadPoolAutoConfiguration {
@Bean
public ThreadPoolTaskExecutor threadPoolTaskExecutor(AsyncThreadPoolProperties properties) {
log.info("start custom async thread pool task executor begin with properties: {} ------", properties);
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(properties.getCorePoolSize());
executor.setMaxPoolSize(properties.getMaxPoolSize());
executor.setQueueCapacity(properties.getQueueCapacity());
executor.setKeepAliveSeconds(properties.getKeepAliveTime());
executor.setThreadNamePrefix(properties.getThreadPrefixName());
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
resources META-INF 创建 spring.factories 文件
写入以下内容
yml
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.ssn.kit.thread.pool.ThreadPoolAutoConfiguration
测试
引入 starter
yml
<dependency>
<groupId>com.ssn.kit</groupId>
<artifactId>corp-kit-thread-pool</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
配置 application.yml
yml
async:
config:
max-pool-size: 4
core-pool-size: 4
queue-capacity: 500
thread-prefix-name: thread-prefix
编写测试 service
java
@Component
@Slf4j
public class AsyncTask {
private final ThreadPoolTaskExecutor threadPoolTaskExecutor;
public AsyncTask(ThreadPoolTaskExecutor threadPoolTaskExecutor) {
this.threadPoolTaskExecutor = threadPoolTaskExecutor;
}
// 使用方式一, 特别要注意这个threadPoolTaskExecutor
@Async("threadPoolTaskExecutor")
public void executor() {
log.info("executor service");
}
// 使用方式二
public void submit() {
threadPoolTaskExecutor.submit(() -> {
log.info("executor submit service");
});
}
}
控制台日志
text
2024-11-13 14:59:22.935 INFO 19232 --- [ thread-prefix1] com.demo.pool.AsyncTask : executor service
2024-11-13 14:59:22.936 INFO 19232 --- [ thread-prefix2] com.demo.pool.AsyncTask : executor submit service