Backend Development 9 min read

Implementing a Dynamic Thread Pool with Nacos in Spring Boot

This article explains how to externalize and dynamically adjust a Spring Boot thread pool's core and maximum sizes using Nacos as a configuration center, allowing runtime changes without service restarts and demonstrating the setup, code, and testing procedures.

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 configured statically, which requires service restarts and lacks flexibility. This article demonstrates how to externalize the core and maximum thread counts to Nacos and dynamically adjust them at runtime without restarting the service.

1. 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>

2. YAML configuration files

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

The bootstrap.yml has higher priority than application.yml , ensuring Nacos configuration is loaded before the application starts.

3. Nacos configuration

In the Nacos console a new configuration is created with Data ID following the pattern ${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension} , e.g., order-service-dev.yml , containing the core and max thread size values.

4. DynamicThreadPool implementation and Nacos listener

@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 values
        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));
                    }
                });
    }

    /** Print current thread pool status */
    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());
    }

    /** Add tasks to the thread pool */
    public void dynamicThreadPoolAddTask(int count) {
        for (int i = 0; i < count; i++) {
            int finalI = i;
            threadPoolExecutor.execute(() -> {
                try {
                    System.out.println(finalI);
                    Thread.sleep(10000);
                } catch (InterruptedException e) { e.printStackTrace(); }
            });
        }
    }

    /** Update core and max size */
    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 thread pool parameters from Nacos.

nacosConfigManager.getConfigService().addListener monitors configuration changes and updates the pool in real time.

5. Controller for testing

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

    /** Print thread pool status */
    @GetMapping("/print")
    public String printThreadPoolStatus() {
        return dynamicThreadPool.printThreadPoolStatus();
    }

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

6. Testing

Start the application and visit http://localhost:8010/threadpool/print to see the current pool configuration. Use http://localhost:8010/threadpool/add?count=20 to add tasks, then print the status again. Adjust the core and max sizes in Nacos (e.g., core=50, max=100) and observe that new tasks are no longer rejected, confirming the dynamic update works.

Conclusion

The article provides a simple implementation of a dynamic thread pool whose core and maximum thread counts can be changed at runtime via Nacos, illustrating the essential annotations, configuration files, listener logic, and testing steps. For production use, further enhancements such as monitoring, alerting, or using more advanced libraries like Hippo4J can be added.

JavaDynamic 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

login 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.