
本文介绍在 spring boot 中实现“接收请求立即响应、后台异步处理、且订单逐个串行执行”的最佳实践,解决并发下单导致的重复处理与数据竞争问题。
本文介绍在 spring boot 中实现“接收请求立即响应、后台异步处理、且订单逐个串行执行”的最佳实践,解决并发下单导致的重复处理与数据竞争问题。
在典型的电商 Web 服务中,我们希望客户端提交订单后能毫秒级返回 HTTP 200 OK,而实际业务逻辑(如库存校验、支付回调、数据库写入等)则在后台异步执行。但若多个订单几乎同时到达,@Async 默认会启用线程池并行处理,极易引发竞态条件——例如两个线程同时读取同一库存余量、各自扣减后写回,最终导致超卖。
直接在 @Async 方法上加 synchronized 是无效且危险的:因为 @Async 会将方法调度到独立线程池中执行,synchronized 锁的是当前对象实例(OrderService Bean),但多个线程调用的是同一个 Bean 的不同代理方法,锁无法跨线程生效;更严重的是,Spring 的 @Transactional 与 synchronized 组合会导致事务上下文丢失(事务绑定在线程局部变量 TransactionSynchronizationManager 中),造成事务不生效或回滚失败。
✅ 正确解法是分离“接收”与“执行”职责,并引入单消费者队列机制:
✅ 推荐方案:使用 TaskExecutor + 内存队列(LinkedBlockingQueue)+ 单线程消费者
@Configuration
public class AsyncOrderConfig {
@Bean("serialOrderExecutor")
public TaskExecutor serialOrderExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(1); // 关键:仅 1 个核心线程 → 强制串行
executor.setMaxPoolSize(1);
executor.setQueueCapacity(Integer.MAX_VALUE);
executor.setThreadNamePrefix("serial-order-");
executor.initialize();
return executor;
}
}@Service
public class OrderService {
private static final Logger log = LoggerFactory.getLogger(OrderService.class);
@Autowired
@Qualifier("serialOrderExecutor")
private TaskExecutor serialExecutor;
// ✅ 非阻塞提交:立即返回,由单线程池顺序消费
public void submitOrderForProcessing(WoocommerceOrder wcOrder) {
serialExecutor.execute(() -> {
try {
processJsonOrder(wcOrder);
} catch (Exception e) {
log.error("Failed to process order: {}", wcOrder.getOrderNumber(), e);
// 可选:发告警、写死信队列、重试等
}
});
}
@Transactional // ✅ 事务可正常工作(在唯一线程内执行)
public void processJsonOrder(WoocommerceOrder wcOrder) {
// 1. 检查订单是否已存在(应用层幂等)
if (orderRepository.existsByExternalId(wcOrder.getOrderId())) {
log.warn("Duplicate order ignored: {}", wcOrder.getOrderId());
return;
}
// 2. 执行完整业务逻辑:库存锁定、生成订单、通知、日志等
OrderEntity order = orderMapper.toEntity(wcOrder);
orderRepository.save(order);
inventoryService.reserveStock(order.getItems());
notificationService.sendOrderConfirmed(order);
}
}@RestController
public class WebShopOrderController {
@Autowired
private OrderService orderService;
@PostMapping(value = "/wc-order", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Void> getWcOrder(@RequestBody String jsonOrder,
@RequestHeader Map<String, String> headers) {
log.info("Order received");
// 解析 JSON → 转为领域对象(建议用 DTO + Jackson)
WoocommerceOrder wcOrder = JsonUtils.parse(jsonOrder, WoocommerceOrder.class);
// ✅ 提交至串行执行器(非阻塞)
orderService.submitOrderForProcessing(wcOrder);
return ResponseEntity.ok().build(); // 立即返回 200 OK
}
}⚠️ 注意事项与增强建议
- 幂等性仍是基石:即使串行,也需在 processJsonOrder 开头通过 external_id 或 order_number 做存在性校验(数据库唯一索引 + 应用层判重),避免因重试或网络重发导致重复。
- 异常不可丢弃:单线程执行器一旦抛出未捕获异常,该线程将终止,后续任务永久阻塞。务必用 try-catch 包裹业务逻辑,并记录错误日志。
- 监控与可观测性:暴露队列长度(((ThreadPoolTaskExecutor) serialExecutor).getThreadPoolExecutor().getQueue().size())和处理耗时指标(如 Micrometer Timer),便于及时发现积压。
-
生产级增强选项:
- 替换内存队列为 Redis List + Lua 脚本消费,支持多实例部署;
- 使用 Kafka 单分区 + 指定 key(如 shop_id)保证同店铺订单有序;
- 引入 Saga 模式处理跨服务长事务。
总结:“异步响应 + 串行处理” ≠ @Async + synchronized,而是应通过可控的单线程任务执行器解耦时序与并发。 这种设计既满足低延迟 API 要求,又从根本上规避了数据竞争,是高可靠订单系统的标准实践。

















