Implementing a Dynamic Thread Pool with Nacos in Spring Boot

This article demonstrates how to create a dynamically configurable thread pool in a Spring Boot backend by leveraging Nacos as a configuration center, covering dependency setup, YAML configuration, Java implementation with @RefreshScope, runtime parameter updates, and a simple controller for testing.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Implementing a Dynamic Thread Pool with Nacos in Spring Boot

In backend development, thread pool parameters are often tuned manually, which can be costly due to required service restarts; this guide shows how to externalize those parameters to Nacos so they can be adjusted at runtime without restarting the service.

Dependencies

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    <version>2021.1</version>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    <version>2021.1</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
</dependency>

YAML Configuration

bootstrap.yml

server:
  port: 8010
  # Application name (used as service name in Nacos)
spring:
  application:
    name: order-service
  cloud:
    nacos:
      discovery:
        namespace: public
        server-addr: 192.168.174.129:8848
      config:
        server-addr: 192.168.174.129:8848
        file-extension: yml

application.yml

spring:
  profiles:
    active: dev

Two YAML files are needed because bootstrap.yml has higher priority than application.yml, ensuring Nacos configuration is loaded before the application starts.

Dynamic Thread Pool Implementation

@RefreshScope
@Configuration
public class DynamicThreadPool implements InitializingBean {
    @Value("${core.size}")
    private String coreSize;
    @Value("${max.size}")
    private String maxSize;
    private static ThreadPoolExecutor threadPoolExecutor;
    @Autowired
    private NacosConfigManager nacosConfigManager;
    @Autowired
    private NacosConfigProperties nacosConfigProperties;

    @Override
    public void afterPropertiesSet() throws Exception {
        // Initialize thread pool from Nacos config
        threadPoolExecutor = new ThreadPoolExecutor(Integer.parseInt(coreSize), Integer.parseInt(maxSize), 10L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(10),
                new ThreadFactoryBuilder().setNameFormat("c_t_%d").build(),
                new RejectedExecutionHandler() {
                    @Override
                    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                        System.out.println("rejected!");
                    }
                });
        // Listen for Nacos config changes
        nacosConfigManager.getConfigService().addListener("order-service-dev.yml", nacosConfigProperties.getGroup(),
                new Listener() {
                    @Override
                    public Executor getExecutor() { return null; }
                    @Override
                    public void receiveConfigInfo(String configInfo) {
                        System.out.println(configInfo);
                        changeThreadPoolConfig(Integer.parseInt(coreSize), Integer.parseInt(maxSize));
                    }
                });
    }

    public String printThreadPoolStatus() {
        return String.format("core_size:%s,thread_current_size:%s;thread_max_size:%s;queue_current_size:%s,total_task_count:%s",
                threadPoolExecutor.getCorePoolSize(), threadPoolExecutor.getActiveCount(),
                threadPoolExecutor.getMaximumPoolSize(), threadPoolExecutor.getQueue().size(),
                threadPoolExecutor.getTaskCount());
    }

    public void dynamicThreadPoolAddTask(int count) {
        for (int i = 0; i < count; i++) {
            int finalI = i;
            threadPoolExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println(finalI);
                        Thread.sleep(10000);
                    } catch (InterruptedException e) { e.printStackTrace(); }
                }
            });
        }
    }

    private void changeThreadPoolConfig(int coreSize, int maxSize) {
        threadPoolExecutor.setCorePoolSize(coreSize);
        threadPoolExecutor.setMaximumPoolSize(maxSize);
    }
}

Key annotations: @RefreshScope enables Nacos dynamic refresh. @Value("${core.size}") and @Value("${max.size}") read the current pool sizes from Nacos. nacosConfigManager.getConfigService().addListener listens for configuration changes and updates the pool in real time.

Controller for Testing

@RestController
@RequestMapping("/threadpool")
public class ThreadPoolController {
    @Autowired
    private DynamicThreadPool dynamicThreadPool;

    @GetMapping("/print")
    public String printThreadPoolStatus() {
        return dynamicThreadPool.printThreadPoolStatus();
    }

    @GetMapping("/add")
    public String dynamicThreadPoolAddTask(@RequestParam int count) {
        dynamicThreadPool.dynamicThreadPoolAddTask(count);
        return String.valueOf(count);
    }
}

Run the application and access http://localhost:8010/threadpool/print to view current pool settings, then call /add?count=20 to submit tasks and observe queue growth and possible rejections.

Testing and Observations

After adding tasks, the console shows "rejected!" when the pool is saturated. By updating Nacos values (e.g., core size 50, max size 100) the rejections disappear, confirming that the dynamic configuration works.

Conclusion

The article provides a straightforward implementation for a dynamically adjustable thread pool using Nacos, illustrating core and max size updates, runtime listening, and a simple REST interface for verification; further enhancements can draw from advanced solutions like Meituan's pooling practice or Hippo4J.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Backend DevelopmentDynamic ConfigurationNacosSpring Bootthread pool
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.