电商订单超时未支付自动关闭的几种实现方案

JAVA herman 305浏览
公告:“业余草”微信公众号提供免费CSDN下载服务(只下Java资源),关注业余草微信公众号,添加作者微信:xttblog,发送下载链接帮助你免费下载!
本博客日IP超过1800,PV 2600 左右,急需赞助商。
极客时间所有课程通过我的二维码购买后返现24元微信红包,请加博主新的微信号:xttblog,之前的微信号好友位已满,备注:返现
所有面试题(java、前端、数据库、springboot等)一网打尽,请关注文末小程序
视频教程免费领

做电商,就会遇到订单超时问题,而且还经常被拿来面试提问!

今天,周末放假,抽时间给大家总结了几种订单超时未支付自动关闭的实现方案。同时,我手机还有几套电商类从零架构到实现的视频教程,如有需要,可以加我的微信号“xttblog”,免费送给大家!

总结来说,订单超时,非常符合业务有“在一段时间之后,完成一个工作任务”的需求。在这类需求中,许多人第一时间想到的就是用定时任务来实现。

定时任务

实现思路比较简单。启动一个计划任务,每隔一定时间处理一次,这种处理方式只是适用比较小而简单的项目。

假设订单表的结构为:

t_order(oid, finish_time, stars, status, …)

然后,定时任务每隔一个 5 分钟(时间自己设定)等会这么做一次:

select oid from t_order where finish_time > 30分钟 and status=0;

update t_order set status=1 where oid in(超时订单id);

如果数据量很大,需要分页查询,分页 update,这将会是一个 for 循环。

但是,这种设计方案有一种明显的不足。

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

但是,也有优势。

  1. 定时任务,实现起来简单。
  2. 也能很好的做分布式集群。

被动取消

这种实现方案和懒加载的思想一直,就是被动的取消订单。只有当用户或商户查询订单信息时,再判断该订单是否超时,如果超时再进行超时逻辑的处理。

但是这种方式依赖于用户的查询操作触发,这也就是说如果用户不进行查询订单的操作,该订单就永远不会被取消。不会取消的订单,也就可能意味着库存可能被占用。

所以,在实际实现上,可能是被动取消 + 定时任务的这种组合实现方式。这种情况下定时任务的时间可以设置的稍微“长“一点。

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

优点,同样是实现起来简单。

延时消息

这种方式是目前比较普遍的实现方式。

延时消息的这种实现方式,包含两个重要的数据结构:

环形队列,例如可以创建一个包含 2400 个 slot 的环形队列(本质是个数组)。
任务集合,环上每一个 slot 是一个 Set。

本质上,就是一个时间轮算法的一个实现。

时间轮算法

时间轮算法可以类比于时钟,如上图箭头(指针)按某一个方向按固定频率轮动,每一次跳动称为一个 tick。这样可以看出定时轮由个3个重要的属性参数,ticksPerWheel(一轮的 tick 数),tickDuration(一个 tick 的持续时间)以及 timeUnit(时间单位),例如当 ticksPerWheel=60,tickDuration=1,timeUnit =秒,这就和现实中的始终的秒针走动完全类似了。

如果当前指针指在 1 上面,我有一个任务需要 4 秒以后执行,那么这个执行的线程回调或者消息将会被放在 5 上。那如果需要在20秒之后执行怎么办,由于这个环形结构槽数只到 8,如果要 20 秒,指针需要多转2圈。位置是在2圈之后的 5 上面(20 % 8 + 1)。

针对时间轮算法或者说延时消息,目前有很多消息队列都支持,比如 RocketMQ,RabbitMQ 等(公众号回复对应关键词获取对应的视频教程)。

扩展 JDK 的延时队列

JDK 自带了一个延时队列 DelayQueue,这是一个无界阻塞队列,该队列只有在延迟期满的时候才能从中获取元素,放入 DelayQueue 中的对象,是必须实现 Delayed 接口的。

DelayQueue 延时队列

如果公司允许,可以在此基础上,扩展成一个分布式的,支持集群的延时队列。但是缺点是,难度较高,小公司根本没有这个机会来做。

Redis 缓存

利用 redis 的 zset。zset是一个有序集合,每一个元素(member)都关联了一个 score,通过 score 排序来取集合中的值。

我们将订单超时时间戳与订单号分别设置为 score 和 member。系统扫描第一个元素判断是否超时,具体如下图所示。

Redis 实现订单超时取消

但是,这种实现方式,在高并发条件下,多消费者可能会取到同一个订单号。当初,我的同事,不得已而又加来一个分布式锁来处理。但是,性能下降严重。后来又做了很多变种,最终还是采用了延时消息。

业余草公众号

最后,欢迎关注我的个人微信公众号:业余草(yyucao)!可加作者微信号1:xmtxtt(5000人已满),微信号2:codedq(5000人已满),微信号3:xttblog(超2800)。备注:“xttblog”,添加博主微信拉你进微信群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作也可添加作者微信进行联系!

本文原文出处:业余草: » 电商订单超时未支付自动关闭的几种实现方案