3

When developing spring boot application services, it is inevitable to use asynchronous tasks and thread pools. The thread pool of spring boot can be customized, so we often see code similar to the following in the project

 @Bean
public Executor taskExecutor() {
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    executor.setCorePoolSize(config.getCorePoolSize());
    executor.setMaxPoolSize(config.getMaxPoolSize());
    executor.setQueueCapacity(config.getQueueCapacity());
    executor.setThreadNamePrefix("TaskExecutePool-");
    executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    executor.setWaitForTasksToCompleteOnShutdown(true);
    executor.initialize();
    return executor;
}

It's convenient to use, but there are several problems with doing so:

  • Developers define thread pools at will in the code, developer A customizes a thread pool, and developer B customizes a thread pool. The resource use of the thread pool is not planned and reasonably arranged, and the cost of later maintenance increases.
  • Once it is found that the number of thread pools is insufficient or the resources are full, it is difficult to adjust the configuration. You can only adjust the code and redeploy.
  • When multiple people write code, it is difficult to count the existence of thread pools in that service, and there are several thread pools.
  • Without tracking the code, it's hard to know how it's being used. With 50 threads defined, is there a waste of resources? Is there a resource waiting?

In order to solve the above problems, I developed a Spring Boot Starter (open source project address: https://gitee.com/hanxt/zimug-monitor-threadpool ), which is easy to integrate into the Spring Boot project. The goal is to make it visible, easy to observe, easy to configure, and easy to use without changing the core implementation of the SpringBoot thread pool .

It should be noted that zimug-monitor-threadpool does not change the implementation of the SpringBoot thread pool, but only adds automatic configuration loading in the initialization phase and status monitoring at runtime. So any discussion of the runtime performance of Spring Boot's thread pool is irrelevant to this article and its implementation.

1. Easy integration and configuration

Obtain the source code through the project address above, and then compile and install the local m2 warehouse with maven. Then import through the following maven coordinates

 <dependency>
    <groupId>com.zimug</groupId>
    <artifactId>zimug-monitor-threadpool</artifactId>
    <version>1.0</version>
</dependency>

As shown in the following configuration of spring boot YAML (application.yml), two thread pools are configured, namely test and test2. The thread pool configuration takes effect when thread-pool.enable=true .

 thread-pool:
  enable: true
  poolLists:
    - poolId: test    #线程池唯一标识
      poolName: 测试1   #线程池的中文描述,比如线程池给谁用?
      coreSize: 5  #线程池初始化核心线程数量
      maxSize: 10  #线程池最大线程容量
      queueCapacity: 10  #线程池等待队列的容量
    - poolId: test2
      poolName: 测试2
      coreSize: 5
      maxSize: 10
      queueCapacity: 10

Understand the above configuration information through the following picture

  • When the number of thread tasks core_size is occupied by active task threads, the thread tasks will be put into the waiting queue (queueCapacity=10)
  • When the waiting queue queueCapacity is also full, the capacity of the thread pool will be expanded
  • The capacity of the thread pool is extended to maxSize. If both maxSize and queueCapacity are full, the task blocks.

2. Easy to use

The usage method is the same as that of the SpringBoot code method to customize the thread pool. Use @Async the value of the annotation is test, and the function identified by this annotation will be put into the test thread pool configured above for execution.

 @Component
public class TestTask {
    @Async("test")   //注意这里,test是线程池配置的poolId
    public Future<String> test() throws Exception {
        System.out.println("当前线程:" + Thread.currentThread().getName());
        return new AsyncResult<>("测试任务");
    }
}

3. Visualization is easy to observe

After introducing zimug-monitor-threadpool into the project, configure the thread pool and use the thread pool. Access the service /pool.html to obtain the thread pool configuration information and runtime status information of the current SpringBoot service.

  • The thread pool ID, description, number of initialization threads, maximum number of threads, and the capacity of the task waiting queue are the static configuration of yaml above
  • The capacity of the current thread pool, that is: the current number of threads in the thread pool (the sum of active + inactive threads)
  • The current number of active threads, that is: the number of threads running program tasks
  • The maximum peak value of active threads in the thread pool. If the value is equal to the number of initialization threads, it means that there has been task waiting, that is, the task is placed in the waiting queue, and the efficiency is low. If the value is greater than the number of initialization threads, it means that the task waiting queue has been full and needs to be expanded. If the value is close to the maximum number of threads, the value of the maximum number of threads needs to be increased.
  • The remaining capacity of the current task waiting queue. The less there is, the more tasks are waiting to be executed.

Fourth, the realization principle

The implementation principle of zimug-monitor-threadpool is also very simple. Let me briefly talk about the principle and refer to the source code for the specific implementation.

  • First, load the yaml configuration information through SpringBoot. After the configuration is loaded, the configuration is automatically loaded. This implementation principle and implementation method are everywhere on the Internet, so I will not write it.
  • After the configuration information is loaded, a new ThreadPoolTaskExecutor object is created, and the bean of the thread pool object is registered in the Spring context through Spring's ConfigurableBeanFactory. The id of the bean is the poolId configuration. It can be used by runtime tasks.

     configurableBeanFactory.registerSingleton(pool.getPoolId(), taskExecutor);
  • When the runtime status of the thread pool needs to be monitored, the thread pool object is obtained through the getBean method, so as to obtain the runtime information and return it to the foreground request.

     ThreadPoolTaskExecutor memThreadPool = (ThreadPoolTaskExecutor) applicationContext.getBean(poolModel.getPoolId());

Welcome to pay attention to my announcement number: Antetokounmpo, reply 003 and present the PDF version of the column "The Way of Docker Cultivation" where this article is located, and more than 30 excellent docker articles. Antetokounmpo Blog: zimug.com


字母哥博客
933 声望1.5k 粉丝