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.
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: ymlapplication.yml
spring:
profiles:
active: devTwo 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.
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.
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.
