SpringBoot基于線程池的訂單創(chuàng)建并行化實踐過程
一、背景
1.1 業(yè)務背景
以電商系統(tǒng)「訂單創(chuàng)建」接口為例
一個用戶下單請求,往往需要完成多個業(yè)務步驟:
- 校驗庫存
- 校驗用戶信息
- 計算訂單價格
- 鎖庫存
- 創(chuàng)建訂單
1.2 問題描述
傳統(tǒng)實現(xiàn)方式:串行執(zhí)行
在高并發(fā)場景下:
- 接口 RT 高
- 線程被長時間占用
- 系統(tǒng)吞吐下降
1.3 技術挑戰(zhàn)
- 哪些任務可以并行?
- 如何安全、高效地并行?
- 如何在 Spring Boot 中正確使用線程池?
二、業(yè)務場景分析與并行拆分
2.1 訂單創(chuàng)建流程拆解
| 步驟 | 是否存在依賴 | 是否可并行 |
|---|---|---|
| 校驗庫存 | 無 | ? |
| 校驗用戶 | 無 | ? |
| 計算價格 | 無 | ? |
| 鎖庫存 | 依賴庫存校驗 | ? |
| 創(chuàng)建訂單 | 依賴前置結果 | ? |
2.2 并行化設計思路
- 無依賴的校驗類任務 → 并行
- 存在業(yè)務依賴的核心流程 → 串行
- 線程池只用于短生命周期任務
三、技術選型與整體設計
3.1 為什么不直接 new Thread?
- 線程創(chuàng)建成本高
- 無法控制并發(fā)量
- 高并發(fā)下容易導致 JVM 失控
3.2 為什么選擇線程池 + CompletableFuture?
- 線程復用,降低系統(tǒng)開銷
- 明確的并發(fā)上限
- 支持任務編排(allOf / thenCombine)
3.3 在 Spring Boot 中的正確姿勢
- 線程池必須交由 Spring 管理
- 使用
ThreadPoolTaskExecutor - 為業(yè)務定制專用線程池,避免互相影響
四、線程池設計與配置(Core)
4.1 線程池配置代碼
@Configuration
public class OrderThreadPoolConfig {
@Bean("orderExecutor")
public ThreadPoolTaskExecutor orderExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(8);
executor.setMaxPoolSize(16);
executor.setQueueCapacity(200);
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("order-create-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
4.2 關鍵參數(shù)說明
corePoolSize:常態(tài)并發(fā)能力maxPoolSize:應對突發(fā)流量queueCapacity:緩沖任務,防止雪崩RejectedExecutionHandler:選擇CallerRunsPolicy實現(xiàn)自然限流
4.3 線程池定位
- Web 請求內(nèi)使用
- IO + 輕計算混合型線程池
- 非長任務、非阻塞型任務
五、核心業(yè)務實現(xiàn)
5.1 校驗 Service(模擬 RPC / DB)
@Service
public class OrderCheckService {
public boolean checkStock(Long skuId) {
sleep(100);
return true;
}
public boolean checkUser(Long userId) {
sleep(80);
return true;
}
public int calcPrice(Long skuId) {
sleep(120);
return 99;
}
private void sleep(long ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
5.2 訂單創(chuàng)建核心邏輯
@Service
public class OrderService {
@Resource(name = "orderExecutor")
private Executor orderExecutor;
@Autowired
private OrderCheckService orderCheckService;
public String createOrder(Long userId, Long skuId) throws Exception {
long start = System.currentTimeMillis();
CompletableFuture<Boolean> stockFuture =
CompletableFuture.supplyAsync(
() -> orderCheckService.checkStock(skuId),
orderExecutor);
CompletableFuture<Boolean> userFuture =
CompletableFuture.supplyAsync(
() -> orderCheckService.checkUser(userId),
orderExecutor);
CompletableFuture<Integer> priceFuture =
CompletableFuture.supplyAsync(
() -> orderCheckService.calcPrice(skuId),
orderExecutor);
CompletableFuture.allOf(
stockFuture, userFuture, priceFuture).join();
if (!stockFuture.get()) {
throw new RuntimeException("庫存不足");
}
int price = priceFuture.get();
lockStock(skuId);
saveOrder(userId, skuId, price);
return "success, cost=" +
(System.currentTimeMillis() - start) + "ms";
}
private void lockStock(Long skuId) {
sleep(50);
}
private void saveOrder(Long userId, Long skuId, int price) {
sleep(80);
}
private void sleep(long ms) {
try {
Thread.sleep(ms);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
5.3 Controller
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
@PostMapping("/create")
public String createOrder(
@RequestParam Long userId,
@RequestParam Long skuId) throws Exception {
return orderService.createOrder(userId, skuId);
}
}
六、JMeter 壓測與結果分析
6.1 壓測配置說明
- 并發(fā)線程數(shù):200
- Ramp-Up:1 秒
6.2 壓測現(xiàn)象
系統(tǒng)啟動初期:
- 接口 RT ≈ 288ms
持續(xù)壓測后:
- RT 逐漸上升
- 峰值約 1888ms
6.3 原因分析
- 每個請求會向線程池提交 3 個并行任務
- 200 個并發(fā)請求 ≈ 600 個線程池任務
- 線程池最大并發(fā)執(zhí)行數(shù)為 16
- 多余任務進入阻塞隊列
- 隊列滿后觸發(fā)
CallerRunsPolicy - 部分任務由 HTTP 工作線程執(zhí)行,導致請求處理時間變長
6.4 工程結論
- RT 上升并不代表線程池失效
- 這是線程池在高并發(fā)下的自我保護行為
- 相比無限創(chuàng)建線程導致系統(tǒng)崩潰,RT 變慢是一種可接受的退化方式
線程池優(yōu)化的是系統(tǒng)吞吐與穩(wěn)定性,而不是在無限并發(fā)下保持恒定響應時間。
七、問題思考
7.1 為什么不用 @Async?
- 難以進行復雜任務編排
- 不利于精細化控制線程池
7.2 為什么不用 parallelStream?
- 使用公共 ForkJoinPool
- 線程資源不可控
7.3 線程池并非萬能
- 仍需配合限流、熔斷等機制
- 核心鏈路與非核心鏈路應區(qū)別對待
總結
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
SpringBoot3.x中spring.factories?SPI?服務發(fā)現(xiàn)機制的改變問題小結
spring.factories其實是SpringBoot提供的SPI機制,底層實現(xiàn)是基于SpringFactoriesLoader檢索ClassLoader中所有jar引入的META-INF/spring.factories文件,這篇文章主要介紹了SpringBoot3.x中spring.factories?SPI?服務發(fā)現(xiàn)機制的改變,需要的朋友可以參考下2023-05-05
Java開發(fā)或調(diào)用WebService的幾種方式總結
java開發(fā)過程中,很多地方都會遇到數(shù)據(jù)傳遞,遠程獲取數(shù)據(jù)問題,這篇文章主要介紹了Java開發(fā)或調(diào)用WebService的幾種方式的相關資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2024-06-06
Java簡單有效實現(xiàn)將PDF轉(zhuǎn)換為TIFF圖片
在日常開發(fā)中,我們常需要將 PDF 轉(zhuǎn)換為高質(zhì)量的 TIFF 圖片,本文將通過 Java 提供一個簡單高效的解決方案,幫助你輕松完成 PDF 到 TIFF 的轉(zhuǎn)換,并支持批量與多頁處理,有需要的可以參考一下2025-09-09
Jenkins Maven pom jar打包未拉取最新包解決辦法
包版本號未變更新后,jenkins打包不會拉取最新包,本文主要介紹了Jenkins Maven pom jar打包未拉取最新包解決辦法,具有一定的參考價值,感興趣的可以了解一下2024-02-02
在mybatis執(zhí)行SQL語句之前進行攔擊處理實例
本篇文章主要介紹了在mybatis執(zhí)行SQL語句之前進行攔擊處理實例,具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-04-04

