专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

Java 面试官:如何取消超时未支付订单?详解 MQ 延迟队列 + Redis 实战方案

Java 面试官:订单下完人跑咧?超时不付款咋办,给他干掉不香嘛!

兄弟们,咱这几年面试走南闯北的,碰见个问题问得是真多,但说实话,这玩意儿就像火锅蘸料,不整一份辣点儿的都对不起这份代码。

今天咱就唠唠——下单不付款,这人跑了,订单咋整?扔那儿臭着?不可能!得想法给他干掉——当然,是那订单哈,不是人。


这事儿为啥老有人问?真就因为容易出事

你想啊——

  • 用户点完付款,结果微信那边卡了,他一关页面,这订单就悬着咧;
  • 后台你不整点东西盯着,他 3 天后付款了,你还真得发货?开玩笑呢;
  • 30分钟不付钱的单子,全给我干掉,这逻辑谁来盯,怎么盯,盯完谁动手取消,咋防止误伤,这些都得说清;

面试官问这玩意儿,说白了就是看你脑袋里有没有货,能不能把复杂事整清,别上来一句“定时任务搞定”,真就当面写答案纸上了兄弟。


几种方案咱细嚼一口口整

1. 定时任务轮询,祖传方式,粗暴顶用但上不了厅堂

@Scheduled(cron = "0 */1 * * * ?")
public void cancelTimeoutOrders() {
   List<Order> orders = orderMapper.findUnpaidBefore(timeNowMinus30Min());
   for (Order o : orders) {
       orderService.cancel(o.getId());
   }
}

说实话,能跑但不太体面;数据库跟打仗似的每分钟扫一遍,全靠硬杠。

缺点贼多:高并发时候直接嘎了;你多实例服务还得加分布式锁,不然就像多个人同时捞锅底,互相抢锅铲,场面太乱了——最后锅也打了,汤也撒了。


2. ScheduledExecutorService,小场面够用,真上生产?你敢我都怕

scheduler.schedule(() -> cancelOrder(orderId), 30, TimeUnit.MINUTES);

单体项目里还行,起个定时器就完事儿,贼快。但你只要一重启,那任务就像老板答应的加薪——消失得连个毛都不剩。

而且你这服务是分布式架构的话,谁负责调度?没商量就上?别人节点也调一遍?人家都开始付款了你还在 cancel……


3. RabbitMQ 死信队列,大厂标配,抗造还骚气

别说,咱真在几个项目里都搞过这玩意儿,稳定得一批。

创建订单那会儿,顺手塞个延迟消息进去 TTL 30 分钟,到点自动扔到死信队列里去,然后由消费者监听拿出来干掉。

Map<String, Object> args = new HashMap<>();
args.put("x-dead-letter-exchange", "order.dlx.exchange");
args.put("x-dead-letter-routing-key", "order.dlx.routingkey");
args.put("x-message-ttl", 1800000); // 30分钟
Queue delayQueue = new Queue("order.delay.queue", true, false, false, args);
rabbitTemplate.convertAndSend("order.exchange", "order.create", orderId);
@RabbitListener(queues = "order.dlx.queue")
public void cancelOrder(String orderId) {
    if (!orderService.isPaid(orderId)) {
        orderService.cancel(orderId);
    }
}

优雅、解耦、还省心。只要 MQ 别挂——挂了你就得祈祷兜底逻辑不掉链子。


4. Redis ZSet,轻量级骚操作,吞吐性能拉满

zadd order:timeout 1715067900 order123
Set<String> ids = redisTemplate.opsForZSet().rangeByScore("order:timeout", 0, now);
for (String id : ids) {
    cancel(id);
    redisTemplate.opsForZSet().remove("order:timeout", id);
}

这个好用是真好用,简单粗暴、能跑贼快——但你要是没持久化,服务一挂,数据没了就全白整。

建议上 Redisson 或者配合持久化搞一搞,要不宕个机你就掉链子了。


幂等性 + 兜底,两个不整你就是在赌命

你 MQ 消息可能丢,你 Redis 线程可能死,你定时任务也可能没触发;这时候怎么办?加兜底定时轮询扫一遍,再救一手。

还有那幂等性,必须整——用户正付款呢,你提前把订单干掉了,人家找你报销你赔不赔?

public void cancel(String orderId) {
    Order o = orderService.getById(orderId);
    if (o.status == WAIT_PAY) {
        orderService.updateStatus(orderId, CANCELLED);
    }
}

面试官要你说,你该咋装?

兄弟你得这么说:

“我们用 RabbitMQ 延迟消息 + 死信队列处理订单取消逻辑,同时 Redis ZSet 做延迟队列兜底保障;每次消费前都会检查订单状态做幂等处理,避免重复取消或误伤,另外我们还做了定时轮询兜底处理 MQ 异常场景。”

面试官一听:好家伙,这人能干活儿。


最后叨叨两句

这东西乍一看简单,真整起来坑贼多。光靠“定时任务”你能唬谁?RabbitMQ、Redis、幂等处理、兜底任务全都安排上你才是懂业务的。

别怕麻烦,怕的是你线上翻车,订单乱了,客服天天找你开会,你小命就交代咧。

实话实说,系统可靠性这玩意就看这细节活儿,要么不干,要干就干成个稳的。

未经允许不得转载:搜云库 » Java 面试官:如何取消超时未支付订单?详解 MQ 延迟队列 + Redis 实战方案

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们