[业务逻辑] 订单超时怎么处理
文章目录
- 1.订单的过程分析
- 2.JDK自带的延时队列 (单机)
- 3.RabbitMQ的延时消息 (消息队列方案)
- 4.RocketMQ的定时消息 (消息队列方案)
- 5.Redis过期监听 (Redis方案)
- 6.定时任务分布式批处理 (扫表轮训方案)
- 7.总结
1.订单的过程分析
一个订单流程中有许多环节要用到超时处理
- 买家超时未付款:比如超过15分钟没有支付,订单自动取消。
- 商家超时未发货:比如商家超过1个月没发货,订单自动取消。
- 买家超时未收货:比如商家发货后,买家没有在14天内点击确认收货,则系统默认自动收货。
超时订单的结局方式:
- 扫表轮训
- 懒删除
- 消息队列实现
- Redis实现
2.JDK自带的延时队列 (单机)
JDK中提供了一种延迟队列数据结构DelayQueue,其本质是封装了PriorityQueue,可以把元素进行排序。
-
把订单插入DelayQueue中,以超时时间作为排序条件,将订单按照超时时间从小到大排序。
-
起一个线程不停轮询队列的头部,如果订单的超时时间到了,就出队进行超时处理,并更新订单状态到数据库中。
-
为了防止机器重启导致内存中的DelayQueue数据丢失,每次机器启动的时候,需要从数据库中初始化未结束的订单,加入到DelayQueue中。
优点:简单,不需要借助其他第三方组件,成本低。
缺点:
-
所有超时处理订单都要加入到DelayQueue中,占用内存大。
-
没法做到分布式处理,只能在集群中选一台leader专门处理,效率低。
-
不适合订单量比较大的场景。
3.RabbitMQ的延时消息 (消息队列方案)
-
RabbitMQ Delayed Message Plugin
-
消息的TTL+死信Exchange
RabbitMQ Delayed Message Plugin是官方提供的延时消息插件,虽然使用起来比较方便,但是不是高可用的,如果节点挂了会导致消息丢失。
消息的TTL+死信Exchange解决方案:
-
定义一个BizQueue,用来接收死信消息,并进行业务消费。
-
定义一个死信交换机(DLXExchange),绑定BizQueue,接收延时队列的消息,并转发给BizQueue。
-
定义一组延时队列DelayQueue_xx,分别配置不同的TTL,用来处理固定延时5s、10s、30s等延时等级,并绑定到DLXExchange。
-
定义DelayExchange,用来接收业务发过来的延时消息,并根据延时时间转发到不同的延时队列中。
优点:可以支持海量延时消息,支持分布式处理。
缺点:
-
不灵活,只能支持固定延时等级。
-
使用复杂,要配置一堆延时队列。
4.RocketMQ的定时消息 (消息队列方案)
只需要在发送消息的时候设置延时时间即可
MessageBuilder messageBuilder = null;
Long deliverTimeStamp = System.currentTimeMillis() + 10L * 60 * 1000; //延迟10分钟
Message message = messageBuilder.setTopic("topic")//设置消息索引键,可根据关键字精确查找某条消息。.setKeys("messageKey")//设置消息Tag,用于消费端根据指定Tag过滤消息。.setTag("messageTag")//设置延时时间.setDeliveryTimestamp(deliverTimeStamp) //消息体.setBody("messageBody".getBytes()).build();
SendReceipt sendReceipt = producer.send(message);
System.out.println(sendReceipt.getMessageId());
RocketMq定时消息的实现:
使用了经典的时间轮算法, 通过TimerWheel来描述时间轮不同的时刻,通过TimerLog来记录不同时刻的消息。
TimerWheel中的每一格代表着一个时刻,同时会有一个firstPos指向这个刻度下所有定时消息的首条TimerLog记录的地址,一个lastPos指向这个刻度下所有定时消息最后一条TimerLog的记录的地址。并且,对于所处于同一个刻度的的消息,其TimerLog会通过prevPos串联成一个链表。
当需要新增一条记录的时候,例如现在我们要新增一个 “1-4”。那么就将新记录的 prevPos 指向当前的 lastPos,即 “1-3”,然后修改 lastPos 指向 “1-4”。这样就将同一个刻度上面的 TimerLog 记录全都串起来了。
优点:
-
精度高,支持任意时刻。
-
使用门槛低,和使用普通消息一样。
缺点
-
使用限制:定时时长最大值24小时。
-
成本高:每个订单需要新增一个定时消息,且不会马上消费,给MQ带来很大的存储成本。
-
同一个时刻大量消息会导致消息延迟:定时消息的实现逻辑需要先经过定时存储等待触发,定时时间到达后才会被投递给消费者。因此,如果将大量定时消息的定时时间设置为同一时刻,则到达该时刻后会有大量消息同时需要被处理,会造成系统压力过大,导致消息分发延迟,影响定时精度。
5.Redis过期监听 (Redis方案)
删除过期的key的时候, 进行判断状态是否是超时状态, 然后进行关闭订单
Redis支持过期监听,也能达到和RocketMQ定时消息一样的能力
1.redis配置文件开启"notify-keyspace-events Ex"
2.监听key的过期回调
@Configuration
public class RedisListenerConfig {@BeanRedisMessageListenerContainer container(RedisConnectionFactory factory){RedisMessageListenerContainer container=new RedisMessageListenerContainer();container.setConnectionFactory(factory);return container;}
}
@Component
public class RedisKeyExpirationListerner extends KeyExpirationEventMessageListener {public RedisKeyExpirationListerner(RedisMessageListenerContainer listenerContainer) {super(listenerContainer);}@Overridepublic void onMessage(Message message, byte[] pattern) {String keyExpira = message.toString();System.out.println("监听到key:" + expiredKey + "已过期");}
}
在实际生产上不推荐
每当我们对一个key设置了过期时间,Redis就会把该key带上过期时间,存到过期字典中,在redisDb中通过expires字段维护:
typedef struct redisDb {dict *dict; /* 维护所有key-value键值对 */dict *expires; /* 过期字典,维护设置失效时间的键 */....
} redisDb;
过期字典本质上是一个链表
-
key是一个指针,指向某个键对象。
-
value是一个long long类型的整数,保存了key的过期时间。
Redis主要使用了定期删除和惰性删除策略来进行过期key的删除
Redis过期删除是不精准的,在订单超时处理的场景下,惰性删除基本上也用不到,无法保证key在过期的时候可以立即删除,更不能保证能立即通知。如果订单量比较大,那么延迟几分钟也是有可能的。
Redis过期通知也是不可靠的,Redis在过期通知的时候,如果应用正好重启了,那么就有可能通知事件就丢了,会导致订单一直无法关闭,有稳定性问题。如果一定要使用Redis过期监听方案,建议再通过定时任务做补偿机制。
如果无法删除的话会导致库存数据始终占着, 但是未支付也未取消支付。
6.定时任务分布式批处理 (扫表轮训方案)
开启一个定时任务去扫描订单表, 获取待支付状态的数据, 判断将一些超时状态的数据进行批量修改状态。
通过定时任务不停轮询数据库的订单,将已经超时的订单捞出来,分发给不同的机器分布式处理:
-
稳定性强:基于通知的方案(比如MQ和Redis),比较担心在各种极端情况下导致通知的事件丢了。使用定时任务跑批,只需要保证业务幂等即可,如果这个批次有些订单没有捞出来,或者处理订单的时候应用重启了,下一个批次还是可以捞出来处理,稳定性非常高。
-
效率高:基于MQ的方案,需要一个订单一个定时消息,consumer处理定时消息的时候也需要一个订单一个订单更新,对数据库tps很高。使用定时任务跑批方案,一次捞出一批订单,处理完了,可以批量更新订单状态,减少数据库的tps。在海量订单处理场景下,批量处理效率最高。
-
可运维:基于数据库存储,可以很方便的对订单进行修改、暂停、取消等操作,所见即所得。如果业务跑失败了,还可以直接通过sql修改数据库来进行批量运维。
-
成本低:相对于其他解决方案要借助第三方存储组件,复用数据库的成本大大降低。
缺点:没法做到精度很高。定时任务的延迟时间,由定时任务的调度周期决定。如果把频率设置很小,就会导致数据库的qps比较高,容易造成数据库压力过大,从而影响线上的正常业务。
所以一般需要抽离出超时中心和超时库来单独做订单的超时调度
如何让超时中心不同的节点协同工作,拉取不同的数据:
通常的解决方案是借助任务调度系统,开源任务调度系统大多支持分片模型,比较适合做分库分表的轮询,比如一个分片代表一张分表。但是如果分表特别多,分片模型配置起来还是比较麻烦的。另外如果只有一张大表,或者超时中心使用其他的存储,这两个模型就不太适合。
阿里巴巴分布式任务调度系统SchedulerX:
- 通过实现map函数,通过代码自行构造分片,SchedulerX会将分片平均分给超时中心的不同节点分布式执行。
2. 通过实现reduce函数,可以做聚合,可以判断这次跑批有哪些分片跑失败了,从而通知下游处理。
使用SchedulerX定时跑批解决方案:
-
免运维、成本低:不需要自建任务调度系统,由云上托管。
-
可观测:提供任务执行的历史记录、查看堆栈、日志服务、链路追踪等能力。
-
高可用:支持同城双活容灾,支持多种渠道的监控报警。
-
混部:可以托管阿里云的机器,也可以托管非阿里云的机器。
7.总结
如果对于超时精度比较高
,超时时间在24小时内,且不会有峰值压力的场景,推荐使用RocketMQ的定时消息解决方案
。
在电商业务
下,许多订单超时场景都在24小时以上,对于超时精度没有那么敏感
,并且有海量订单需要批处理
,推荐使用基于定时任务的跑批解决方案
。
- 扫表轮训: 定时任务分布式批处理, 阿里使用SchedulerX
- 懒删除: 通过设置一个数据库的状态, 用户查询订单的时候去判断状态看是否关闭订单
- 消息队列实现: RabbitMQ的ttl和延迟队列, RocketMQ的定时消息
- Redis实现: 删除策略实现不乐观
相关文章:

[业务逻辑] 订单超时怎么处理
文章目录1.订单的过程分析2.JDK自带的延时队列 (单机)3.RabbitMQ的延时消息 (消息队列方案)4.RocketMQ的定时消息 (消息队列方案)5.Redis过期监听 (Redis方案)6.定时任务分布式批处理 (扫表轮训方案)7.总结1.订单的过程分析 一个订单流程中有许多环节要用到超时处理 买家超时未…...

iOS上架及证书最新创建流程
目前使用uniapp框架开发app,大大节省了我们兼容多端应用的工作量和人手,所以目前非常缺乏ios上架和证书创建流程流程的文档假如你没有任何的打包或上架经验,参考本文有很大的收益。通常申请ios证书和上架ipa应用,是需要MAC电脑的&…...
python入门
Python是一种高级编程语言,由荷兰计算机科学家Guido van Rossum于1991年发明。Python语言具有简洁、清晰和易于阅读的语法,同时也拥有广泛的应用领域,包括Web开发、数据分析、人工智能、科学计算等。Python的特点是能够快速开发原型和简单易读…...

Linux部署java项目
Linux部署java项目启动虚拟机这部分的操作之前学习虚拟机时已经做过,可以参照之前的笔记即可推荐大家重新解压纯净版的RockyLinux来实现启动后登录rockylinuxsudo su -修改root用户密码passwd下面就切换到客户端软件连接虚拟机ifconfigifconfig | more查看ip地址使用Bvssh软件连…...
elisp 从简单实例开始.
elisp 从简单实例开始. 我们怎样用elisp 与电脑交互,先从简单实例开始, 逐渐掌握它的几个对象. 与电脑交互,总要有输入,输出,先看两个简单例子. 输入从minibuffer,输出可以是minibuffer 或者缓冲区. 一: 从minibuffer 中输入, 在指定缓冲中插入文字(insert)x ;;;;;;;;;;;;;;;;…...
ThreeJS加载geojson数据实现3D地图
ThreeJS加载geojson数据实现3D地图,主要通过借助geojson地理信息数据转摩托尔坐标实现,中间借助了d3.js的地图处理方法,最后通过threejs渲染到页面上: 通过平台获取GeoJson格式的行政区域借助d3的方法,将坐标系转摩托尔坐标利用ThreeJS中的自定义Shape,绘制地图利用Three…...

深度学习无监督磁共振重建方法调研(二)
深度学习无监督磁共振重建方法调研(二)Self-supervised learning of physics-guided reconstruction neural networks without fully sampled reference data(Magnetic Resonance in Medicine 2020)模型设计实验结果PARCEL: Physi…...

蓝桥杯入门即劝退(十九)两两交换链表
-----持续更新蓝桥杯入门系列算法实例-------- 如果你也喜欢Java和算法,欢迎订阅专栏共同学习交流! 你的点赞、关注、评论、是我创作的动力! -------希望我的文章对你有所帮助-------- 一、题目描述 给你一个链表,两两交换其中…...
【Java 面试合集】接口以及抽象类
接口以及抽象类 1. 概述 嗨,【Java 面试合集】又来了,今天给大家分享的内容是接口以及抽象类。一看这个概念很多人都知道,但是方方面面的细节不一定知道哦,今天我们就从方方面面的细节来讲讲 2. 相同点: 都是上层的抽…...
LeetCode 2391. 收集垃圾的最少总时间
给你一个下标从 0 开始的字符串数组 garbage ,其中 garbage[i] 表示第 i 个房子的垃圾集合。garbage[i] 只包含字符 ‘M’ ,‘P’ 和 ‘G’ ,但可能包含多个相同字符,每个字符分别表示一单位的金属、纸和玻璃。垃圾车收拾 一 单位…...

【PMP考试最新解读】第七版《PMBOK》应该如何备考?(含最新资料)
PMP新版大纲加入了ACP敏捷管理的内容,而且还不少,敏捷混合题型占到了 50%,前不久官方也发了通知8月启用第七版《PMBOK》,大家都觉得考试难度提升了,我从新考纲考完下来,最开始也被折磨过一段时间࿰…...

金三银四软件测试面试如何拿捏面试官?【接口测试篇】
九、接口测试 9.1 接口测试怎么测 (jmeter版本) 首先开发会给我们一个接口文档,我们根据开发给的接口文档,进行测试点的分析,主要是考虑正常场景与异常场景,正常场景,条件的组合,…...
Hive基操
数据交换 //hive导出到hdfs /outstudentpt 目录 0: jdbc:hive2://guo146:10000> export table student_pt to /outstudentpt; //从hdfs导入到hive 0: jdbc:hive2://guo146:10000> import table studentpt from /outstudentpt; 数据排序 Order by会对所给的全部数据进行…...

CSS(配合html的网页编程)
续上一篇博客,CSS是前端三大将中其中的一位,主要负责前端的皮,也就是负责html的装饰.一、基本语法规则也就是:选择器若干属性声明(选中一个元素然然后进行属性声明)CSS代码是放在style标签中,它可以放在head中也可以放在body中 ,可以放到代码的任意位置.color也就是设置想要输入…...

MATLAB/Simulink 通信原理及仿真学习(三)
文章目录MATLAB/Simulink 通信原理及仿真学习(三)3. 通信信号与系统分析3.1 离散信号和系统3.1.1 离散信号3.1.2 离散时间信号3.1.3 信号的能量和功率3.2 傅里叶(Fourier)分析3.2.1 连续时间信号的Fourier变换3.2.2 离散时间信号的…...

如何解决过拟合与欠拟合,及理解k折交叉验证
模型欠拟合:在训练集以及测试集上同时具有较⾼的误差,此时模型的偏差较⼤; 模型过拟合:在训练集上具有较低的误差,在测试集上具有较⾼的误差,此时模型的⽅差较⼤。 如何解决⽋拟合: 添加其他特…...

Kotlin 34. recyclerView 案例:显示列表
Kotlin 案例1. recyclerView:显示列表 这里,我们将通过几个案例来介绍如何使用recyclerView。RecyclerView 是 ListView 的高级版本。 当我们有很长的项目列表需要显示的时候,我们就可以使用 RecyclerView。 它具有重用其视图的能力。 在 Re…...
JAVA练习58-汉明距离、颠倒二进制位
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 前言 一、题目1-汉明距离 1.题目描述 2.思路与代码 2.1 思路 2.2 代码 二、题目2-颠倒二进制位 1.题目描述 2.思路与代码 2.1 思路 2.2 代码 总结 前言 提示…...

优炫数据库百城巡展,成都首站圆满举行
2月17日,由四川省大数据发展研究会、北京优炫软件股份有限公司联合举办的“首届四川省推进信息技术应用创新产业服务研讨会暨优炫数据库百城巡展成都首站隆重举行。此次活动是优炫数据库百城巡展的起点站,更是国产数据库市场美好乐章的一次强力鸣奏。 来…...

【20230210】二叉树小结
二叉树的种类二叉树的主要形式:满二叉树和完全二叉树。满二叉树深度为k,有2^k-1个节点的二叉树完全二叉树除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。二叉搜索树…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql
智慧工地管理云平台系统,智慧工地全套源码,java版智慧工地源码,支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求,提供“平台网络终端”的整体解决方案,提供劳务管理、视频管理、智能监测、绿色施工、安全管…...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...
Nginx server_name 配置说明
Nginx 是一个高性能的反向代理和负载均衡服务器,其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机(Virtual Host)。 1. 简介 Nginx 使用 server_name 指令来确定…...
解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错
出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...

ElasticSearch搜索引擎之倒排索引及其底层算法
文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...
多模态图像修复系统:基于深度学习的图片修复实现
多模态图像修复系统:基于深度学习的图片修复实现 1. 系统概述 本系统使用多模态大模型(Stable Diffusion Inpainting)实现图像修复功能,结合文本描述和图片输入,对指定区域进行内容修复。系统包含完整的数据处理、模型训练、推理部署流程。 import torch import numpy …...
Caliper 配置文件解析:fisco-bcos.json
config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...

【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制
目录 节点的功能承载层(GATT/Adv)局限性: 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能,如 Configuration …...