SpringBoot基于线程池的订单创建并行化实践过程

 更新时间:2025年12月26日 08:52:13   作者:kkkkkkkkl24  
文章介绍了电商系统订单创建接口的并行处理方案,通过分析业务流程、选择合适的线程池和技术,解决了高并发场景下的性能问题

一、背景

1.1 业务背景

以电商系统「订单创建」接口为例

一个用户下单请求,往往需要完成多个业务步骤:

  • 校验库存
  • 校验用户信息
  • 计算订单价格
  • 锁库存
  • 创建订单

1.2 问题描述

传统实现方式:串行执行

在高并发场景下:

  • 接口 RT 高
  • 线程被长时间占用
  • 系统吞吐下降

1.3 技术挑战

  • 哪些任务可以并行?
  • 如何安全、高效地并行?
  • 如何在 Spring Boot 中正确使用线程池

二、业务场景分析与并行拆分

2.1 订单创建流程拆解

步骤是否存在依赖是否可并行
校验库存
校验用户
计算价格
锁库存依赖库存校验
创建订单依赖前置结果

2.2 并行化设计思路

  • 无依赖的校验类任务 → 并行
  • 存在业务依赖的核心流程 → 串行
  • 线程池只用于短生命周期任务

三、技术选型与整体设计

3.1 为什么不直接 new Thread?

  • 线程创建成本高
  • 无法控制并发量
  • 高并发下容易导致 JVM 失控

3.2 为什么选择线程池 + CompletableFuture?

  • 线程复用,降低系统开销
  • 明确的并发上限
  • 支持任务编排(allOf / thenCombine)

3.3 在 Spring Boot 中的正确姿势

  • 线程池必须交由 Spring 管理
  • 使用 ThreadPoolTaskExecutor
  • 为业务定制专用线程池,避免互相影响

四、线程池设计与配置(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 关键参数说明

  • corePoolSize:常态并发能力
  • maxPoolSize:应对突发流量
  • queueCapacity:缓冲任务,防止雪崩
  • RejectedExecutionHandler:选择 CallerRunsPolicy 实现自然限流

4.3 线程池定位

  • Web 请求内使用
  • IO + 轻计算混合型线程池
  • 非长任务、非阻塞型任务

五、核心业务实现

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 订单创建核心逻辑

@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 压测配置说明

  • 并发线程数:200
  • Ramp-Up:1 秒

6.2 压测现象

系统启动初期:

  • 接口 RT ≈ 288ms

持续压测后:

  • RT 逐渐上升
  • 峰值约 1888ms

6.3 原因分析

  • 每个请求会向线程池提交 3 个并行任务
  • 200 个并发请求 ≈ 600 个线程池任务
  • 线程池最大并发执行数为 16
  • 多余任务进入阻塞队列
  • 队列满后触发CallerRunsPolicy
  • 部分任务由 HTTP 工作线程执行,导致请求处理时间变长

6.4 工程结论

  • RT 上升并不代表线程池失效
  • 这是线程池在高并发下的自我保护行为
  • 相比无限创建线程导致系统崩溃,RT 变慢是一种可接受的退化方式

线程池优化的是系统吞吐与稳定性,而不是在无限并发下保持恒定响应时间。

七、问题思考

7.1 为什么不用 @Async?

  • 难以进行复杂任务编排
  • 不利于精细化控制线程池

7.2 为什么不用 parallelStream?

  • 使用公共 ForkJoinPool
  • 线程资源不可控

7.3 线程池并非万能

  • 仍需配合限流、熔断等机制
  • 核心链路与非核心链路应区别对待

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java管道流实现线程间通信过程解析

    Java管道流实现线程间通信过程解析

    这篇文章主要介绍了Java管道流实现线程间通信过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-03-03
  • Java Swagger使用教程

    Java Swagger使用教程

    Swagger是一个规范和完整的框架,用于生成、描述、调用和可视化 Restful 风格的 Web 服务。总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的方法、参数和模型紧密集成到服务器端的代码,允许API来始终保持同步
    2022-07-07
  • java 获取服务器真实IP的实例

    java 获取服务器真实IP的实例

    这篇文章主要介绍了java 获取服务器真实IP的实例的相关资料,这里提供实现方法帮助大家学习理解这部分内容,需要的朋友可以参考下
    2017-08-08
  • Java的MyBatis+Spring框架中使用数据访问对象DAO模式的方法

    Java的MyBatis+Spring框架中使用数据访问对象DAO模式的方法

    Data Access Object数据访问对象模式在Java操作数据库部分的程序设计中经常被使用到,这里我们就来看一下Java的MyBatis+Spring框架中使用数据访问对象DAO模式的方法:
    2016-06-06
  • springboot之Duration(java.time.Duration)在yml properties中的配置方式

    springboot之Duration(java.time.Duration)在yml properties中

    这篇文章主要介绍了springboot之Duration(java.time.Duration)在yml properties中的配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • Spring mvc是如何实现与数据库的前后端的连接操作的?

    Spring mvc是如何实现与数据库的前后端的连接操作的?

    今天给大家带来的是关于Spring mvc的相关知识,文章围绕着Spring mvc是如何实现与数据库的前后端的连接操作的展开,文中有非常详细的介绍及代码示例,需要的朋友可以参考下
    2021-06-06
  • JVM中堆内存和栈内存的区别

    JVM中堆内存和栈内存的区别

    本文主要介绍了JVM中堆内存和栈内存的区别,具有很好的参考价值,下面跟着小编一起来看下吧
    2017-02-02
  • Spring AI ectorStore的使用流程

    Spring AI ectorStore的使用流程

    SpringAI中的VectorStore是一种用于存储和检索高维向量数据的数据库或存储解决方案,它在AI应用中发挥着至关重要的作用,本文给大家介绍Spring AI ectorStore的使用流程,感兴趣的朋友一起看看吧
    2025-03-03
  • Java加载资源文件时的路径问题的解决办法

    Java加载资源文件时的路径问题的解决办法

    今天偶然看到一篇关于tomcat加载servlet的文章,不由得想起了java加载资源文件的路径问题,资源文件可以使xml,properties,图片等,可以是任何文件
    2013-04-04
  • java如何删除非空文件夹

    java如何删除非空文件夹

    这篇文章主要介绍了java如何删除非空文件夹问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-06-06

最新评论