未支付订单超时处理分析

如购买后未支付订单,需要在十分钟后回滚状态等这类问题。

有什么方法解决

定时任务

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

好处是实现简单、也好做分布式集群。

但是坏处也很明显:

时效性差,会有一定的延迟,这个延迟时间最大就是每隔一定时间的大小,如果你设置每分钟定时轮询一次,那么理论上订单取消时间的最大误差就有一分钟。

效率低。

对数据库的压力比较大。

懒加载

也就是不做处理,只有在用户重新点开的时候在判断。

这种方法简单,不过会造成回库问题。

也可以用定时任务加懒加载。(redis删除策略?

上面两种方法在实际场景用不到的,看看拓展下思路就行了。

map

用map或者队列放每个消息对象(注意map也能做到)。再为每个消息都启动一个timer,时间到了就让timer直接调用回调函数处理业务。
这种方案timer线程不可能多到和消息数量一样大。不实际
【改进】
一个改进是:给每个消息加个剩余时间字段,只用一个timer,将每个消息排序,剩余时间短的在前面,这样每次只需要比较一个时间。
另一个改进是:作为生产者把时间到的消息放到一个队列里,让超时消息处理方成为消费者来消费消息,这样就直接跟rabbitmq结合了起来。

消息队列

此处使用rabbitmq(因为本人只使用过这个- -)

死信队列

它是 通过使用TTLDXL这两个属性间接实现的。

大概是:当消息在一个队列超时指定时间后,将会变成一个死信,然后它会被重新publish到另一个交换机上,这个交换机就是死信交换机。

TTL 顾名思义:指的是消息的存活时间,RabbitMQ可以通过x-message-tt参数来设置指定Queue(队列)和 Message(消息)上消息的存活时间,它的值是一个非负整数,单位为微秒。

DLX:死信交换机,绑定在死信交换机上的即死信队列。RabbitMQ的Queue(队列)可以配置两个参数x-dead-letter-exchange和x-dead-letter-routing-key(可选),一旦队列内出现了Dead Letter(死信),则按照这两个参数可以将消息重新路由到另一个Exchange(交换机),让消息重新被消费。

x-dead-letter-exchange:队列中出现Dead Letter后将Dead Letter重新路由转发到指定 exchange(交换机)。

x-dead-letter-routing-key:指定routing-key发送,一般为要指定转发的队列。

如下所示:

/*** 延时队列*/
@Bean(name = "order.delay.queue")
public Queue getMessageQueue() {return QueueBuilder.durable(RabbitConstant.DEAD_LETTER_QUEUE)// 配置到期后转发的交换.withArgument("x-dead-letter-exchange", "order.close.exchange")// 配置到期后转发的路由键.withArgument("x-dead-letter-routing-key", "order.close.queue").build();
}

延迟队列

每个订单创建的时候发延时消息。到点判断是不是要过期。

如果用它实现只需要下载一个插件,然后配置好时间就行了,非常方便。

下面是使用Spring Cloud Stream + Rabbit MQ 的demo

yaml配置:

spring:cloud:stream:bindings:testOutput:  destination: testBuycontent-type: application/jsontype: x-delayed-messagetestInput:  destination: testBuycontent-type: application/jsongroup: checkOrderrabbit:bindings:testOutput:producer:delayedExchange: truetestInput:consumer:delayedExchange: true
    public void send(TbWechatPayOrder payOrder) {log.info("准备发送延迟队列信息:{}", LocalTime.now());// 十分钟Message<TbWechatPayOrder> build = MessageBuilder.withPayload(payOrder).setHeader("x-delay", 60000 * 10).build();testOutput.send(build);}

使用它的好处是:

消息队列本就是专门使用收发信息的,专业对口,异步处理,性能较好。

而且还能保证消息可靠性等。

redis

轮询

我们知道 Redis 有一个有序集合的数据结构 ZSet,ZSet 中每个元素都有一个对应 Score,ZSet 中所有元素是按照其 Score 进行排序的。

那么我们可以通过以下这几个操作使用 Redis 的 ZSet 来实现一个延迟队列:

入队操作:ZADD KEY timestamp task, 我们将需要处理的任务,按其需要延迟处理时间作为 Score 加入到 ZSet 中。Redis 的 ZAdd 的时间复杂度是O(logN),N是 ZSet 中元素个数,因此我们能相对比较高效的进行入队操作。
起一个进程定时(比如每隔一秒)通过ZREANGEBYSCORE方法查询 ZSet 中 Score 最小的元素,具体操作为:ZRANGEBYSCORE KEY -inf +inf limit 0 1 WITHSCORES。查询结果有两种情况:
a. 查询出的分数小于等于当前时间戳,说明到这个任务需要执行的时间了,则去异步处理该任务;
b. 查询出的分数大于当前时间戳,由于刚刚的查询操作取出来的是分数最小的元素,所以说明 ZSet 中所有的任务都还没有到需要执行的时间,则休眠一秒后继续查询;
同样的,ZRANGEBYSCORE操作的时间复杂度为O(logN + M),其中N为 ZSet 中元素个数,M为查询的元素个数,因此我们定时查询操作也是比较高效的。

它有以下这些好处

Redis用来进行实现延时队列是具有这些优势的:

  1. Redis zset支持高性能的 score 排序。
  2. Redis是在内存上进行操作的,速度非常快。
  3. Redis可以搭建集群,当消息很多时候,我们可以用集群来提高消息处理的速度,提高可用性。
  4. Redis具有持久化机制,当出现故障的时候,可以通过AOF和RDB方式来对数据进行恢复,保证了数据的可靠性

另外比rabbitmq简洁 不用定义那么多队列和交换机
缺点是涉及到一个分布式锁的问题
也不可能单独部署一个服务节点专门处理这个订单超时问题的 ,并且redis在主从架构下宕机,容易丢失锁。

过期事件监听

利用Redis的事件监听机制, 还有另外一种方式实现延迟处理.

Redis可以根据需要, 修改redis.conf配置, 实现对一些事件的监听, 其中就包括key过期事件.

redis.conf 配置:

notify-keyspace-events Ex

这个事件监听是通过pubsub机制实现的, 所以业务代码中实现对事件的订阅, 就可以知道哪个key过期了。

有了上述事件监听基础, 将延期事件对应key存入Redis, 并根据延迟时间设置key过期时间, 当key过期时, 便能触发监听事件, 完成延迟处理逻辑。

redisson

百科:

Redisson是架设在Redis基础上的一个Java驻内存数据网格(In-Memory Data Grid)。【Redis官方推荐】

Redisson在基于NIO的Netty框架上,充分的利用了Redis键值数据库提供的一系列优势,在Java实用工具包中常用接口的基础上,为使用者提供了一系列具有分布式特性的常用工具类。使得原本作为协调单机多线程并发程序的工具包获得了协调分布式多机多线程并发系统的能力,大大降低了设计和研发大规模分布式系统的难度。同时结合各富特色的分布式服务,更进一步简化了分布式环境中程序相互之间的协作。

demo:

都在代码里,手动泪目:https://cloud.tencent.com/developer/article/1589324

或者找官方文档。

redis主从架构丢锁问题:

问题:
比如某个主redis节点A加锁成功之后宕机了,导致锁丢失,然后另一个redis从节点B发生主从切换,接着节点B又再次加锁成功,这就会产生多个节点加锁成功,出现问题。

解决:
使用Redlock解决。

Redlock思想:
使用N个完全独立,没有主从关系的主redis节点,保证他们大多数情况下不会宕机。
然后对每个主redis节点进行加锁,只有超过半数,也就是(N/2 + 1)的主redis节点加锁成功,才算成功,否则一律算失败。
失败的话,还是会到每个主redis节点进行释放锁,不管之前有没有加锁成功。

为什么需要分布式锁:

如下:

成员变量 A 存在 JVM1、JVM2、JVM3 三个 JVM 内存中

成员变量 A 同时都会在 JVM 分配一块内存,三个请求发过来同时对这个变量操作,显然结果是不对的

不是同时发过来,三个请求分别操作三个不同 JVM 内存区域的数据,变量 A 之间不存在共享,也不具有可见性,处理的结果也是不对的
注:该成员变量 A 是一个有状态的对象

可参考:

https://blog.csdn.net/qq_30736263/article/details/104009072

https://blog.csdn.net/yue_2018/article/details/89784454

其他

kafka netty也可以

总结

有句话说的好:没有最好的,只有最合适的

以上方法除了定时任务和懒加载不实际外,其他的都是可以被使用到的。

如在一个小而简单的项目里,原本项目就没有redis、消息队列等,贸然加入不仅添加部署成本,还可能出现其他问题,那么就可以用map。

在一个不需要考虑或者已经解决了分布式锁问题的项目里,使用redis做延时队列也是个简单高效的选择。

在因为分布式锁问题而放弃redis时,可以使用消息队列,如rabbitmq的死信、延时等。

资料参考

rabbitmq、redis参考:http://www.dockone.io/article/10139

redis过期事件监听参考:https://blog.csdn.net/weixin_41751625/article/details/107554132

分布式锁参考:https://www.cnblogs.com/gxyandwmm/p/9588383.html

最后

本人只是个菜鸟,这篇分析可能写的有不够完善的地方,如哪里有错误或者不明了的,欢迎大家斧正。

,可以使用消息队列,如rabbitmq的死信、延时等。

资料参考

rabbitmq、redis参考:http://www.dockone.io/article/10139

redis过期事件监听参考:https://blog.csdn.net/weixin_41751625/article/details/107554132

分布式锁参考:https://www.cnblogs.com/gxyandwmm/p/9588383.html

最后

本人只是个菜鸟,这篇分析可能写的有不够完善的地方,如哪里有错误或者不明了的,欢迎大家斧正。

未支付订单超时处理分析相关推荐

  1. 1.超时未支付订单处理

    1.超时未支付订单处理 1.1 需求分析 超过限定时间并未支付的订单,我们需要进行超时订单的处理:先调用微信支付api,查询该订单的支付状态.如果未支付调用关闭订单的api,并修改订单状态为已关闭,并 ...

  2. B2C电商项目(第十三天、超时未支付订单处理、订单批量发货、确认收货与自动收货)

    订单处理 课程内容: 通过 rabbitmq的延迟消息完成超时订单处理 完成批量发货功能,了解第三方物流系统 完成自动收货功能 一.超时未支付订单处理 1.1 需求分析 超过限定时间并未支付的订单,我 ...

  3. 定时任务 - 定时关闭超期未支付订单

    /*** 关闭超时未支付订单*/ public void closeOrder(); @Transactional(propagation = Propagation.REQUIRED) @Overr ...

  4. 解决阿里云存在未支付订单请支付或作废后再下单

    阿里云服务器或其他云资源无法立即购买,提示"您选择的资源存在未支付订单,请支付或作废后再下单!"什么原因?这是由于你的阿里云账号之前已经创建了该订单,只是订单没有支付,所以无法再次 ...

  5. java实现订单未支付失效_未支付订单30分钟后,自动取消

    未支付订单30分钟后,自动取消 生成订单时发起延时30分钟的任务 /** * 取消订单的任务 * @Title: startCancelOrderTask * @Description: 取消订单的任 ...

  6. TP5.1 实现超时未支付订单自动关闭

    对于这个需求,我以前写过Laravel版本的.今天想在TP5.1中实现这个功能,但是网上基本没什么教程可供参考,所以写篇文章仅供大家学习. 一.前台 1.先来加载订单确认页面 当下单成功后,通过 js ...

  7. 从零开发短视频电商 30分钟未支付订单自动关闭、七天自动确认收货等延迟任务问题

    文章目录 常见延迟任务 常见解决方案 主动形式 被动形式 基于Redis实现ZSet的方式.键空间通知的方式 ZSet的方式 键空间通知的方式 RocketMQ延迟消息 延迟消息级别配置 客户端发送延 ...

  8. 阿里二面:针对一个100W/秒的未支付订单取消场景,说说你的解决方案

    在工作或面试时,常常被问起关于消息队列.MQ或延时消息场景相关的问题. 最常见的延时消息场景,一般有以下几个: 下单后超过30分钟未支付,后台自动取消订单: 订单超过48h未评论,系统自动生成一条好评 ...

  9. Redis key过期事件监听实现 - 30分钟自动取消未支付订单

    目录 一.前言 二.实现方案分析 三.Redis key过期事件方案实现步骤 3.1 Redis 安装步骤详见 3.2 修改 Redis 配置 3.3 在获取支付链接视图中设置key过期事件 3.4 ...

  10. springboot 模拟秒杀 分布式锁 以及 延时取消未支付订单

    简易描述秒杀系统的几个主要特点,分布式情况下使用锁,订单超时未支付使用mq的延时队列取消 maven依赖 <dependency><groupId>org.springfram ...

最新文章

  1. java继承详解加练习题
  2. java中的取模_Java 中的取模和取余
  3. mysql的最佳索引攻略
  4. Quartus ii与Modelsim-altera 6.5b联调前仿真
  5. 南工程c语言实验报告,Linux环境下-C语言编程实验报告(1)(总4页).doc
  6. using namespace std
  7. 牛客题霸-SQL篇——1~10题
  8. Python基础知识回顾及scrapy框架爬虫基础
  9. 项目开发文档是必须的
  10. Linux/windows下java调用lingo
  11. ttf字体文件抽取自己想要的字
  12. aistudio下载文件避免报错
  13. 分清视频质量中的各种电影视频格式标
  14. 傻瓜式自制鼠标光标,超简单
  15. win10安装apache环境
  16. 打造卓越的 Android 游戏体验
  17. 高通QCA平台常见wifi命令使用
  18. jQuery中的end()方法
  19. python3切割圆形图片
  20. nginx.conf文件(原始无修改)

热门文章

  1. 我喜欢的乐队-Descending
  2. 7.14英语学习:英国拍争议文物
  3. 没有寻线仪怎么找网线_乱七八糟的网线怎么找?寻线仪来帮你
  4. MATLAB求解一阶RC电路和二阶RLC电路
  5. 海康 linux java demo_Java 实现 海康摄像头抓拍图像 Windows、Linux
  6. TMS320C5509A 控制DDS AD9854芯片进行AM幅度调制时的FIR滤波处理
  7. kali linux 网络渗透测试学习笔记(三)社会工程学之Java攻击:钓鱼网站制作
  8. 王艾辉:下方重点关注3140 上方3190 破位则追
  9. linux yum安装scp,CentOS安装scp命令的软件包openssh-clients
  10. ios13 微信提示音插件_iOS13免越狱修改微信提示音方法!亲测有用!