订单超时-延时任务处理

博客介绍了几种订单取消方案。定时任务时效性差、效率低且对数据库压力大;被动取消依赖用户查询,会产生额外影响并影响体验。还介绍了延时消息,如 RocketMQ、RabbitMQ 支持的时间轮算法,以及 JDK 的延时队列 DelayQueue,最后提及用 Redis 缓存 zset 处理订单超时。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、定时任务

时效性差,会有一定的延迟,这个延迟时间最大就是每隔一定时间的大小,如果你设置每分钟定时轮询一次,那么理论上订单取消时间的最大误差就有一分钟,当然也可能更大,比如一分钟之内有大量数据,但是一分钟没处理完,那么下一分钟的就会顺延。效率低。对数据库的压力比较大。

2、被动取消

这种方式依赖于用户的查询操作触发,这也就是说如果用户不进行查询订单的操作,该订单就永远不会被取消。不会取消的订单,也就可能意味着库存可能被占用,也可以是被动取消 +定时任务的这种组合实现方式。这种情况下定时任务的时间可以设置的稍微“长“一点。

缺点:
会产生额外影响,比如统计,订单数,库存等产生影响。
影响用户体验,用户打开订单列表可能要处理大量数据,影响显示的实时性。

3.延时消息

针对时间轮算法或者说延时消息,目前有很多消息队列都支持,
比如 RocketMQ,RabbitMQ

  • 死信队列: TTL消息的存活时间、DLX即死信交换机
  • 时间轮:

12:00:00 时启动定时器。 插入任务「12:41:00」时,处理为 c(环数) = (12:41:00 - 12:00:00)/60 - 0 = 41,k(slot key) = (12:41:00 - 12:00:00)%60 - 0= 0;
每一个执行周期到对应的 slot 时,该 slot 对应的链表中所有节点剩余轮数-1 执行到 12:19:01 时,时间轮情况如下:

在这里插入图片描述
一个1天的定时器,时间轮分为3个,小时轮(24 slot)、分钟轮(60 slot)、秒轮(60 slot) 方便理解
在这里插入图片描述

4、延时队列

DelayQueue必须实现 Delayed 接口的

  • JDK 中提供了一组实现延迟队列的API,位于Java.util.concurrent包下DelayQueue

  • DelayQueue是一个BlockingQueue(无界阻塞)队列,

  • 它本质就是封装了一个PriorityQueue(优先队列),PriorityQueue内部使用完全二叉堆来实现队列元素排序。

  • 在向DelayQueue队列中添加元素时,会给元素一个Delay(延迟时间)作为排序条件,队列中最小的元素会优先放在队首。

  • 队列中的元素只有到了Delay时间才允许从队列中取出。

  • 队列中可以放基本数据类型或自定义实体类,在存放基本数据类型时,优先队列中元素默认升序排列,自定义实体类就需要我们根据类属性值比较计算了。

5、Redis 缓存

zset是一个有序集合,每一个元素(member)都关联了一个 score,通过 score 排序来取集合中的值。我们将订单超时时间戳与订单号分别设置为 score 和 member。系统扫描第一个元素判断是否超时。

### 使用延时队列实现订单超时自动取消功能 #### RabbitMQ 实现方案 为了实现订单超时自动取消的功能,可以通过配置RabbitMQ中的死信交换机(Dead-Letter Exchange, DLX)以及设置消息TTL(Time-To-Live),从而构建延迟队列机制。当一条消息进入正常的工作队列后,在其存活时间内如果没有被消费,则会被转发到指定的DLX关联的目标队列中去。 对于订单系统而言: - **创建两个队列**:一个是普通的`orderQueue`负责接收新产生的订单;另一个则是作为死信处理用途的`deadLetterQueue`专门用来保存那些因超过规定期限而未完成付款流程的记录[^1]。 ```java // Java伪代码展示如何声明带有TTL属性的消息队列 Map<String, Object> args = new HashMap<>(); args.put("x-message-ttl", 60000); // 设置过期时间为1分钟 channel.queueDeclare("orderQueue", true, false, false, args); ``` 一旦某个订单超过了设定好的等待周期仍未得到确认支付的信息反馈,那么这条数据就会自动转移到预先定义好用于后续逻辑判断(如触发邮件通知用户或直接删除无效交易)的特殊通道里——即所谓的“死信”。 这种设计模式不仅能够有效减少数据库查询压力,同时也简化了应用程序内部状态管理复杂度,提高了系统的可扩展性和灵活性[^2]。 #### Redis 实现方案 另一种常见的做法是利用Redis来充当延时任务调度器的角色。通过向sorted set结构添加成员项并赋予相应的score值表示预期执行时刻戳,以此达到按计划顺序触发事件的效果。每当到达预设时间节点时,程序可以从集合头部取出最早一批待办事项加以处置,直至全部清理完毕为止。 针对上述提到的电商应用场景来说,可以考虑如下操作步骤: - 将每笔新开立的销售单据连同它的截止时限一同存入名为`pending_orders`的数据表; - 定义后台线程定期轮询检查当前时间是否大于等于任何一项已登记条目的结束标志位; - 如果条件满足则立即采取行动终止该事务,并更新相关联的产品库存数量等信息[^3]。 ```bash # Bash命令模拟向redis sorted set插入元素的过程 EPOCH=$(date +%s) EXPIRE_TIME=$(( EPOCH + 60 )) # 设定为一分钟后的Unix timestamp形式 redis-cli zadd pending_orders $EXPIRE_TIME order_id_12345 ``` 综上所述,无论是选用AMQP协议下的开源中间件还是基于内存键值存储的服务端组件,都能很好地支持起这类异步非阻塞式的业务需求处理方式。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值