【面试】阿里面试官:让你实现一个订单超时取消功能,怎么设计?

订单超时取消功能设计方案

需求分析

订单超时取消功能的核心需求是:当用户下单后未在规定时间内支付(如30分钟),系统自动取消订单并释放库存。该功能需兼顾高并发、可靠性、可扩展性。


技术方案大纲

方案一:定时任务扫描

通过定时任务轮询数据库,检查未支付订单的超时状态。

实现步骤

  1. 数据库设计:订单表需包含status(状态)、create_time(创建时间)、expire_time(过期时间)字段。
  2. 定时任务:每分钟扫描status='待支付' AND expire_time <= NOW()的订单。
  3. 取消逻辑:批量更新订单状态为"已取消",并调用库存释放接口。

伪代码示例

// 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)。

实现步骤

  1. 订单创建时:发送延迟消息到队列,设置TTL为30分钟。
  2. 消息消费:消息到期后触发消费者处理取消逻辑。
  3. 幂等处理:检查订单状态是否仍为"待支付"。

伪代码示例

// 发送延迟消息
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)功能。

实现步骤

  1. 订单创建时:在Redis设置键order:{orderId},超时时间30分钟。
  2. 配置Redis:启用notify-keyspace-events Ex
  3. 监听事件:订阅__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监听 + 延迟队列兜底 + 定时任务补偿。
所以,你学到了吗?记得点赞关注一下

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小冷coding

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值