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 using Nacos as a configuration center, covering Maven dependencies, YAML configuration files, Nacos data IDs, Java implementation with listeners, a REST controller for testing, and practical verification steps.
Hello everyone, I am Chen.
In backend development, thread pools are frequently used, but configuring core parameters often relies on experience and requires service restarts, which is costly. Placing thread‑pool settings on a platform side and allowing developers to adjust them dynamically based on runtime conditions can solve this problem.
This article uses Nacos as a service configuration center and shows, through modifying the core and maximum thread numbers, how to implement a simple dynamic thread pool.
Code Implementation
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. Configure YML files
bootstrap.yml:
server:
port: 8010
# Application name (Nacos treats this as the service name)
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: ymlapplication.yml:
spring:
profiles:
active: devWhy two YML files? In Spring Boot the loading order gives bootstrap higher priority than application . Nacos must fetch configuration from the config center before the application starts, so the bootstrap file ensures early loading.
3. Nacos configuration
Log in to the Nacos console and create a new configuration (see image). The Data ID follows the pattern ${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension} , which in this article becomes order-service-dev.yml . Only two parameters – core thread count and maximum thread count – are configured.
4. Thread‑pool configuration and Nacos change 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 according to 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!");
}
});
// Nacos config change listener
nacosConfigManager.getConfigService().addListener("order-service-dev.yml", nacosConfigProperties.getGroup(),
new Listener() {
@Override
public Executor getExecutor() {
return null;
}
@Override
public void receiveConfigInfo(String configInfo) {
// Config changed, update thread pool
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(new Runnable() {
@Override
public void run() {
try {
System.out.println(finalI);
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
/** Modify core parameters */
private void changeThreadPoolConfig(int coreSize, int maxSize) {
threadPoolExecutor.setCorePoolSize(coreSize);
threadPoolExecutor.setMaximumPoolSize(maxSize);
}
}Key points:
@RefreshScope enables Nacos dynamic refresh.
@Value("${max.size}") and @Value("${core.size}") read the values from Nacos and update them in real time.
nacosConfigManager.getConfigService().addListener listens for configuration changes and adjusts the thread pool accordingly.
5. Controller
A simple REST controller is added to observe the dynamic changes.
@RestController
@RequestMapping("/threadpool")
public class ThreadPoolController {
@Autowired
private DynamicThreadPool dynamicThreadPool;
/** Print current thread‑pool status */
@GetMapping("/print")
public String printThreadPoolStatus() {
return dynamicThreadPool.printThreadPoolStatus();
}
/** Add tasks to the thread pool */
@GetMapping("/add")
public String dynamicThreadPoolAddTask(int count) {
dynamicThreadPool.dynamicThreadPoolAddTask(count);
return String.valueOf(count);
}
}6. Test
Start the project and visit http://localhost:8010/threadpool/print to see the current thread‑pool configuration (see image).
Call /threadpool/add?count=20 to add 20 tasks and then re‑print the status (see image). You will see tasks queuing.
Repeatedly invoke the /add endpoint; when the console shows rejection messages, adjust the Nacos configuration.
After changing the Nacos values to core=50 and max=100, the rejections disappear and the printed status confirms the new parameters.
Summary
This article provides a straightforward implementation of a dynamic thread pool whose core and maximum thread numbers can be adjusted at runtime via Nacos. For deeper insights into thread‑pool design, refer to Meituan’s article (https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html) and consider integrating monitoring and alerting to build a complete dynamic thread‑pool product.
Other excellent solutions exist, such as Hippo4J, which offers both middleware‑free and Nacos/Apollo‑based implementations, while dynamic‑tp defaults to Nacos or Apollo.
Final Note (Please support)
If this article helped you, please like, follow, share, or bookmark – your support keeps me going!
My knowledge‑sharing community is also open for a 199 CNY subscription, offering extensive resources like Spring full‑stack projects, massive data sharding practice, DDD micro‑service series, and more.
For more details, visit the links above.
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
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.