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实现添加条形码到PDF表格的方法详解

    Java实现添加条形码到PDF表格的方法详解

    条码的应用已深入生活和工作的方方面面。本文以操作PDF文件为例,介绍如何利用Java语言在编辑表格时,向单元格中添加条形码,感兴趣的可以学习一下
    2022-06-06
  • Java详解swagger2如何配置使用

    Java详解swagger2如何配置使用

    编写和维护接口文档是每个程序员的职责,根据Swagger2可以快速帮助我们编写最新的API接口文档,再也不用担心开会前仍忙于整理各种资料了,间接提升了团队开发的沟通效率
    2022-06-06
  • JDBC简介_动力节点Java学院整理

    JDBC简介_动力节点Java学院整理

    什么是JDBC?这篇文章就为大家详细介绍了Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-07-07
  • SpringBoot3.x中spring.factories SPI 服务发现机制的改变问题小结

    SpringBoot3.x中spring.factories SPI 服务发现机制的改变问题小结

    spring.factories其实是SpringBoot提供的SPI机制,底层实现是基于SpringFactoriesLoader检索ClassLoader中所有jar引入的META-INF/spring.factories文件,这篇文章主要介绍了SpringBoot3.x中spring.factories SPI 服务发现机制的改变,需要的朋友可以参考下
    2023-05-05
  • IDEA入门级使用教程你居然还在用eclipse?

    IDEA入门级使用教程你居然还在用eclipse?

    上个月,idea的使用量超越eclipse的消息席卷了整个IT界,idea到底好在哪里呢?下面小编通过本文给大家详细介绍下IDEA入门级使用教程,非常详细,感兴趣的朋友一起看看吧
    2020-10-10
  • Java开发或调用WebService的几种方式总结

    Java开发或调用WebService的几种方式总结

    java开发过程中,很多地方都会遇到数据传递,远程获取数据问题,这篇文章主要介绍了Java开发或调用WebService的几种方式的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-06-06
  • Java简单有效实现将PDF转换为TIFF图片

    Java简单有效实现将PDF转换为TIFF图片

    在日常开发中,我们常需要将 PDF 转换为高质量的 TIFF 图片,本文将通过 Java 提供一个简单高效的解决方案,帮助你轻松完成 PDF 到 TIFF 的转换,并支持批量与多页处理,有需要的可以参考一下
    2025-09-09
  • JavaAPI的使用方法详解

    JavaAPI的使用方法详解

    这篇文章主要介绍了JavaAPI的使用方法详解,还是比较不错的,这里分享给大家,供需要的朋友参考。
    2017-11-11
  • Jenkins Maven pom jar打包未拉取最新包解决办法

    Jenkins Maven pom jar打包未拉取最新包解决办法

    包版本号未变更新后,jenkins打包不会拉取最新包,本文主要介绍了Jenkins Maven pom jar打包未拉取最新包解决办法,具有一定的参考价值,感兴趣的可以了解一下
    2024-02-02
  • 在mybatis执行SQL语句之前进行拦击处理实例

    在mybatis执行SQL语句之前进行拦击处理实例

    本篇文章主要介绍了在mybatis执行SQL语句之前进行拦击处理实例,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
    2017-04-04

最新评论