Building a Dynamic Thread Pool with SpringBoot and Nacos
This article demonstrates how to create a dynamically configurable thread pool in a SpringBoot application by leveraging Nacos as a centralized configuration center, covering dependencies, YAML setup, Java implementation, controller endpoints, and runtime testing.
Background
Thread pool parameters in backend services are often tuned by experience and require service restarts when changed, leading to high operational cost. By moving the thread pool configuration to a platform side and using Nacos as a configuration center, core and maximum thread counts can be adjusted dynamically at runtime.
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: ymlapplication.yml
spring:
profiles:
active: devBootstrap has higher priority than application.yml, ensuring Nacos configuration is loaded before the application starts.
Nacos Configuration
In the Nacos console a new configuration is created. The Data ID follows the pattern
${spring.application.name}-${spring.profile.active}.${spring.cloud.nacos.config.file-extension}, resulting in order-service-dev.yml for this example. The file contains only two entries: core.size and max.size.
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!");
}
});
// 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();
}
}
});
}
}
/** Change 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("${max.size}") and @Value("${core.size}") read the parameters from Nacos. nacosConfigManager.getConfigService().addListener registers a listener to apply changes without restarting.
Controller for Observation
@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 */
@GetMapping("/add")
public String dynamicThreadPoolAddTask(int count) {
dynamicThreadPool.dynamicThreadPoolAddTask(count);
return String.valueOf(count);
}
}Testing Procedure
Start the application and call http://localhost:8010/threadpool/print – the response shows the initial core and max values defined in Nacos.
Invoke http://localhost:8010/threadpool/add?count=20 to submit 20 tasks. Re‑print the status and observe queued tasks and possible rejection messages.
After several calls, all tasks are rejected. Update the Nacos configuration, setting core.size=50 and max.size=100, then repeat the /add calls. No rejections appear and the printed status reflects the new parameters.
Conclusion
The article provides a straightforward implementation that allows the core and maximum thread counts of a ThreadPoolExecutor to be modified at runtime via Nacos. For production‑grade solutions, refer to Meituan's detailed article on Java thread‑pool practice.
Reference: https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Architect's Guide
Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.
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.
