订单超时取消功能设计方案
需求分析
订单超时取消功能的核心需求是:当用户下单后未在规定时间内支付(如30分钟),系统自动取消订单并释放库存。该功能需兼顾高并发、可靠性、可扩展性。
技术方案大纲
方案一:定时任务扫描
通过定时任务轮询数据库,检查未支付订单的超时状态。
实现步骤
- 数据库设计:订单表需包含
status(状态)、create_time(创建时间)、expire_time(过期时间)字段。 - 定时任务:每分钟扫描
status='待支付' AND expire_time <= NOW()的订单。 - 取消逻辑:批量更新订单状态为"已取消",并调用库存释放接口。
伪代码示例
// Spring Boot定时任务
@Scheduled(fixedRate = 60000)
public void cancelExpiredOrders() {
List<Order> orders = orderMapper.selectExpiredOrders();
orders.forEach(order -> {
order.setStatus("CANCELED");
orderMapper.update(order);
inventoryService.releaseStock(order.getItems());
});
}
优缺点
- 优点:实现简单,依赖少
- 缺点:高频扫描可能影响数据库性能,时效性差(最多延迟1分钟)
方案二:延迟队列
利用消息队列的延迟投递特性(如RabbitMQ的Dead Letter Exchange)。
实现步骤
- 订单创建时:发送延迟消息到队列,设置TTL为30分钟。
- 消息消费:消息到期后触发消费者处理取消逻辑。
- 幂等处理:检查订单状态是否仍为"待支付"。
伪代码示例
// 发送延迟消息
rabbitTemplate.convertAndSend(
"order.delay.exchange",
"order.delay.routingkey",
orderId,
message -> {
message.getMessageProperties().setDelay(30 * 60 * 1000);
return message;
}
);
// 消费者
@RabbitListener(queues = "order.cancel.queue")
public void handleCancelOrder(String orderId) {
Order order = orderService.getById(orderId);
if (order.getStatus().equals("PENDING")) {
orderService.cancelOrder(orderId);
}
}
优缺点
- 优点:时效精确,降低数据库压力
- 缺点:RabbitMQ需插件支持延迟队列,消息可能丢失
方案三:Redis过期监听
利用Redis的键空间通知(Keyspace Notification)功能。
实现步骤
- 订单创建时:在Redis设置键
order:{orderId},超时时间30分钟。 - 配置Redis:启用
notify-keyspace-events Ex。 - 监听事件:订阅
__keyevent@0__:expired频道处理取消逻辑。
伪代码示例
// 设置Redis键
redisTemplate.opsForValue().set(
"order:" + orderId,
"PENDING",
30,
TimeUnit.MINUTES
);
// 监听器配置
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory factory) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(factory);
container.addMessageListener((message, pattern) -> {
String expiredKey = message.toString();
if (expiredKey.startsWith("order:")) {
String orderId = expiredKey.split(":")[1];
orderService.cancelOrder(orderId);
}
}, new PatternTopic("__keyevent@0__:expired"));
return container;
}
优缺点
- 优点:实时性高,性能好
- 缺点:Redis重启可能导致数据丢失,需处理消息堆积
生产级优化建议
补偿机制
增加定时任务作为兜底方案,防止消息丢失场景。
分布式锁
取消操作需加分布式锁,避免并发问题。
// Redisson分布式锁示例
RLock lock = redissonClient.getLock("order:cancel:" + orderId);
try {
if (lock.tryLock(5, 10, TimeUnit.SECONDS)) {
// 处理取消逻辑
}
} finally {
lock.unlock();
}
状态机控制
使用状态机(如Spring State Machine)规范订单状态流转。
监控报警
对取消失败订单进行监控,触发人工干预流程。
方案选型对比
| 指标 | 定时任务 | 延迟队列 | Redis监听 |
|---|---|---|---|
| 实时性 | 差(分钟级) | 精确(秒级) | 精确(秒级) |
| 可靠性 | 高 | 依赖MQ可靠性 | 依赖Redis可靠性 |
| 复杂度 | 低 | 中 | 中 |
| 适用场景 | 低频业务 | 中高频业务 | 高频业务 |
高频场景推荐组合方案:Redis监听 + 延迟队列兜底 + 定时任务补偿。
所以,你学到了吗?记得点赞关注一下

2403

被折叠的 条评论
为什么被折叠?



