过期订单自动取消的几种实现方式
前言
在电商系统中,经常会遇到这样的场景:当订单未支付超时,需要自动取消订单,否则会一直占用库存等资源。
那么实现订单未支付超时,自动取消的方式有哪些呢?这里列举些我了解的几种方式。
扫表实现
原理: 通过定时任务轮询扫描订单表,超时的批量修改状态
优点: 实现非常简单
缺点: 大量数据集,对服务器内存消耗大。 数据库频繁查询,订单量大的情况下,IO是瓶颈。 存在延迟,间隔短则耗资源,间隔长则时效性差,两者是一对矛盾。 不易控制,随着定时业务的增多和细化,每个业务都要对订单重复扫描,引发查询浪费
java 延迟队列实现
原理: 通过DelayQueue,每下一单,放入一个订单元素并实现getDelay()方法,方法返回该元素距离失效还剩余的时 间,当<=0时元素就失效,就可以从队列中获取到。启用线程池对数据监听,一旦捕获失效订单,取出之后,调用 取消逻辑进行处理。
优点: 基于jvm内存,效率高,任务触发时间延迟低。
缺点: 存在jvm内存中,服务器重启后,数据全部丢失。 依赖代码硬编码,集群扩展麻烦 依赖jvm内存,如果订单量过大,无界队列内容扩充,容易出现OOM 需要代码实现,多线程处理业务,复杂度较高 多线程处理时,数据频繁触发等待和唤醒,多了无谓的竞争
消息队列实现
原理:设置两个队列,每下一单放一条进延迟队列,设定过期时间。消息一旦过期,获取并放入工作队列,由consumer 获取,唤起超时处理逻辑。
**如果采用的是RabbitMQ,其本身没有直接支持延迟队列功能,可以针对Queue和Message设置 x-message-ttl, 用消息的生存时间,和死信队列来实现,具体有两种手段, A: 通过队列属性设置,队列中所有消息都有相同的过 期时间,粗粒度,编码简单 B: 对消息进行单独设置,每条消息TTL可以不同,细粒度,但编码稍微复杂。 **
优点:可以随时在队列移除,实现实时取消订单,及时恢复订单占用的资源(如商品)
消息存储在mq中,不占用应用服务器资源
异步化处理,一旦处理能力不足,consumer集群可以很方便的扩容
缺点:可能会导致消息大量堆积 mq服务器一旦故障重启后,持久化的队列过期时间会被重新计算,造成精度不足 死信消息可能会导致监控系统频繁预警
redis实现
原理: 利用redis的notify-keyspace-events,该选项默认为空,改为Ex开启过期事件,配置消息监听。每下一单在redis中 放置一个key(如订单id),并设置过期时间。
优点: 消息都存储在Redis中,不占用应用内存。 外部redis存储,应用down机不会丢失数据。 做集群扩展相当方便 依赖redis超时,时间准确度高
缺点: 订单量大时,每一单都要存储redis内存,需要大量redis服务器资源
被动取消
原理:在每次用户查询订单的时候,判断订单时间,超时则同时完成订单取消业务。
优点:实现极其简单 不会有额外的性能付出 不依赖任何外部中间件,只是应用逻辑的处理
缺点:延迟度不可控,如果用户一直没触发查询,则订单一直挂着,既不支付也未取消,库存也就被占着
- Title: 过期订单自动取消的几种实现方式
- Author: 薛定谔的汪
- Created at : 2019-05-01 13:20:16
- Updated at : 2023-11-17 19:37:37
- Link: https://www.zhengyk.cn/2019/05/01/mq/rabbitmq/cancel-expired-order/
- License: This work is licensed under CC BY-NC-SA 4.0.