Implementing a Dynamic Thread Pool with Nacos in Spring Cloud
This article demonstrates how to build a dynamically configurable thread pool in a Spring Cloud backend by using Nacos as a configuration center, covering dependency setup, YAML files, Java implementation, controller exposure, testing steps, and practical tips for runtime adjustments.
In backend development, thread pool parameters are often tuned manually, which is costly because changes usually require a service restart. Placing the thread pool configuration on a platform side allows developers to adjust core and maximum thread counts dynamically.
The example uses Nacos as a service configuration center to modify the core and maximum thread numbers of a thread pool at runtime.
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
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: ymlapplication.yml
spring:
profiles:
active: devBootstrap has higher priority than application.yml, ensuring Nacos configuration is loaded before the Spring Boot application starts.
3. Nacos Configuration
Create a configuration in the Nacos console. The Data ID follows the pattern ${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension} , e.g., order-service-dev.yml . Only two parameters are set: core.size and max.size .
4. 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 {
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 to 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(() -> {
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);
}
}5. 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);
}
}6. Testing Steps
Start the application and call http://localhost:8010/threadpool/print to view current thread pool settings.
Invoke http://localhost:8010/threadpool/add?count=20 to add 20 tasks, then re‑print the status. Adjust the Nacos configuration (e.g., core size to 50, max size to 100) when tasks are rejected, and observe the pool adapting without restart.
7. Summary
The article provides a simple implementation of a dynamic thread pool whose core and maximum thread counts can be changed at runtime via Nacos. For production‑grade solutions, refer to Meituan’s detailed article on Java thread‑pool practices and integrate monitoring and alerting.
Architect
Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.
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.