当前位置: 首页 > news >正文

订单超时自动取消的实践方案

1、定时任务方案

方案流程:

  1. 每隔 30 秒查询数据库,取出最近的 N 条未支付的订单。

  2. 遍历查询出来的订单列表,判断当前时间减去订单的创建时间是否超过了支付超时时间,如果超时则对该订单执行取消操作。

定时任务方案工程实现相对简单,但这种方案会间隔对数据库造成一定的 IO 压力。特别是当订单量数据量非常高时,高频次的查询对数据库的性能是个不小的考验。

定时任务方案从功能模块角度来讲,包含调度层业务逻辑层两部分。

图片

网上有很多的定时任务实现策略,我们可以简单划分为单机版集群版

2、定时任务方案:单机版

我们可以使用 Timer 、ScheduledEexcutorService、Quartz 非常容易的实现定时任务。

图片

但笔者并不推荐使用单机版的方案,举个简单的例子:

图片

假设我们应用 A 通过 Quartz 调度三个定时任务 A、B、C  ,当集群部署时,可能出现多台不同机器实例同时执行任务的风险。

此时,我们可以通过加锁的方式适当规避,见下图:

图片

但这种方式并不优雅,同时定时任务应用内调度层会经常空跑,我们预期是希望三个定时任务 A、B、C 能均匀分布应用 A的不同实例内。

好,接下来,笔者会介绍亲身经历的三种集群定时任务。

3、定时任务方案:集群版

图片

3.1 Quartz + JDBCJobStore

Quartz 可以支持集群模式,集群模式需要在数据库中添加11张表,对业务系统有一定的侵入性。

图片

笔者曾经服务的一家彩票公司,订单调度中心就是使用 Quartz 的集群模式,实现日均百万订单的调度处理。

需要特别注意的是:

基于底层数据库悲观锁的机制,Quartz 的集群模式性能并不高,假如执行频率高的任务数超过达到一定数量,存在性能问题。

3.1 Elastic-Job

ElasticJob 定位为轻量级无中心化解决方案,使用 jar 的形式提供分布式任务的协调服务。

ElasticJob 从本质上来讲 ,底层任务调度还是通过 Quartz ,它的优势在于可以依赖 Zookeeper 这个大杀器 ,将任务通过负载均衡算法分配给应用内的 Quartz Scheduler 容器,

举例:应用A有五个任务需要执行,分别是 A,B,C,D,E。任务E需要分成四个子任务,应用部署在两台机器上。

图片

图中,应用 A 在启动后, 5个任务通过 Zookeeper 协调后被分配到两台机器上,通过 Quartz Scheduler 分开执行不同的任务。

相比 Quartz 集群模式,ElasticJob 的可扩展性更高,同时因为是本地内存存储 JOB,性能非常好。

但是 ElasticJob 的控制台非常粗糙,主要原因还是基于它的实现机制 (Quartz +  zookeeper)。

通过控制 zookeeper 节点来间接操作应用内任务执行情况,但这样非常不灵活,所以笔者认为 ElasticJob 更多的还是定位于框架,而不是一个调度平台

3.3 任务调度平台

笔者非常认可任务调度平台这种模式。XXL-JOB 是一个使用最广泛的分布式任务调度平台

图片

图片

业务系统和调度平台分开部署,我们在调度中心上配置应用以及其定时任务,当任务需要执行时,调度平台会触发业务系统的任务,业务系统执行完任务之后,反馈给调度平台任务执行的结果。

业务系统和调度平台都可以水平扩展实现高可用,同时在调度平台可以配置灵活的调度策略(比如重试机制广播模式等)。

XXL-JOB  并不完美,因为底层依然是基于数据库悲观锁的机制,虽然通过时间轮的方式做了一定程度的优化,但依然会有性能瓶颈。

很多公司比如神州专车、美团都有自己自研的任务调度平台。这种模式非常适合多团队协作,便于大规模调度任务的统一管理。

4、延时消息方案

延时消息是一种非常优雅的模式。订单服务生成订单后,发送一条延时消息到消息队列。消息队列在消息到达支付过期时间时,将消息投递给消费者,消费者收到消息之后,判断订单状态是否为已支付,假如未支付,则执行取消订单的逻辑。

图片

4.1 消息队列 RocketMQ

RocketMQ 4.X 生产者发送延迟消息代码如下:

Message msg = new Message();
msg.setTopic("TopicA");
msg.setTags("Tag");
msg.setBody("this is a delay message".getBytes());
//设置延迟level为5,对应延迟1分钟
msg.setDelayTimeLevel(5);
producer.send(msg);

RocketMQ 4.X 版本默认支持 18 个 level 的延迟消息, 通过 broker 端的 messageDelayLevel 配置项确定的。

图片

RocketMQ 5.X 版本支持任意时刻延迟消息,客户端在构造消息时提供了 3 个 API 来指定延迟时间或定时时间。

图片

假如技术团队基础架构能力很强,笔者非常推荐使用 RocketMQ 5.X 的延迟消息功能。

4.2 自研延迟服务

基于 RocketMQ 4 内置的延迟消息只能支持几个固定的延迟级别,快手、滴滴开发了单独的 Delay Server 来调度延迟消息。

图片

上图这个结构没有直接将延迟消息发到 Delay Server,而是更换 Topic 以后存入 RocketMQ。这样的好处是可以复用现有的消息发送接口(以及上面的所有扩展能力)。对业务来说,只需要在构造消息的时候额外指定一个延迟时间字段即可,其它用法都不变。

自研单独的  Delay Server 不仅可以适配 RocketMQ 4.X , 也可以适配 Kafka ,同时,也可以具有非常高的性能,说实话,这个是一个非常实用且灵活的方案。

4.3 Redis 延迟队列

Redis 延迟队列是一个轻量级的解决方案,开源成熟的实现是 Redission 。

图片

图中,我们定义两个集合:

1、zset 集合

生产者将任务信息发送到 zset 集合,value 是任务编号,score 是任务执行时间戳。

2、list 集合

守护线程检测  zset 集合中到期的任务,若任务到期,将任务编号转移到 list 集合 , 消费者从 list 集合弹出任务,并执行任务逻辑。

笔者需要强调的是:

Redis 虽然可以实现延迟消息的功能,但 Redis 并不是真正意义上的消息队列,在使用过程中还是有小概率会丢失消息

5、最佳实践

5.1 并发口诀:一锁二判三更新

不管我们使用定时任务还是延迟消息时,不可避免的会遇到并发执行任务的情况 (比如重复消费、调度重试等)。

当我们执行任务时,我们可以按照一锁二判三更新这个口诀来处理。

  1. 锁定当前需要处理的订单。

  2. 判断订单是否已经更新过对应状态了

  3. 如果订单之前没有更新过状态了,可以更新并完成相关业务逻辑,否则本次不能更新,也不能完成业务逻辑。

  4. 释放当前订单的锁。

图片

图片

伪代码

5.2 兜底意识 + 配置监控

虽然我们提到了很多的实现策略,现实实战时依然容易出现问题,比如不合理的操作导致消息丢失。

因此,我们应该具备兜底意识

假如少量消息丢失,我们可以通过每天凌晨跑一次任务,批量将这些未处理的订单批量取消。这种兜底行为工程实现简单,同时对系统影响很小。

还有一点,就是配置监控

笔者曾经自研过任务调度系统,应用 A 接入后,从控制台发现每隔 2 个小时调度应用 A 的任务时,经常发生超时,通过分析,发现应用 A 线程出现了死锁。

这种问题出现的几率非常高,因此配置监控特别要必要。

对业务系统来讲,监控分为两个层面:系统监控业务监控

  • 系统监控

在条件允许的情况下,建议关注性能监控,方法可用性监控,方法调用次数监控这三大类。

图片

性能监控

上图是性能监控的示例图,性能监控不同时间段性能分布,实时统计 TP99、TP999 、AVG 、MAX 等维度指标,这也是性能调优的重点关注对象。

  • 业务监控

业务监控功能是从业务角度出发,各个应用系统需要从业务层面进行哪些监控,以及提供怎样的业务层面的监控功能支持业务相关的应用系统。

具体就是对业务数据,业务功能进行监控,实时收集业务流程的数据,并根据设置的策略对业务流程中不符合预期的部分进行预警和报警,并对收集到业务监控数据进行集中统一的存储和各种方式进行展示。

比如订单系统中有一个定时结算的服务,每两分钟执行一次。我们可以在定时任务 JOB 中添加埋点,并配置业务监控,假如十分钟该定时任务没有执行,则发送邮件,短信给相关负责人。

6、总结

这篇文章,笔者总结了订单超时自动取消方案的两种流派:定时任务延迟消息

1、定时任务

定时任务实现策略,我们可以简单划分为单机版集群版

笔者并不认可单机版,背八股文当然可以,订单自动取消这个业务场景,生产环境还是要慎重。

集群版有三种方式:Quartz + JDBCJobStoreElasticJob 、XXL-JOB 。

每种方式各有优缺点,笔者更倾向于任务调度平台 XXL-JOB 这种方式。

2、延迟消息

延时消息是一种非常优雅的模式。本文介绍了三种方式:消息队列 RocketMQ自研延迟服务Redis 延迟队列

假如技术团队基础架构能力很强,笔者推荐使用 RocketMQ 或者自研延迟服务。

假如技术团队仅仅想用轻量级的实现,可以选择 Redis 延迟队列。

不管是使用定时任务还是延迟消息,架构的稳定性还需要注意如下两点:

1、并发口诀:一锁二判三更新 ;

2、兜底意识 + 配置监控

相关文章:

订单超时自动取消的实践方案

1、定时任务方案 方案流程: 每隔 30 秒查询数据库,取出最近的 N 条未支付的订单。 遍历查询出来的订单列表,判断当前时间减去订单的创建时间是否超过了支付超时时间,如果超时则对该订单执行取消操作。 定时任务方案工程实现相…...

【gin框架入门】

1.介绍 Gin 是一个用 Golang编写的 高性能的web 框架, 由于http路由的优化,速度提高了近 40 倍。 Gin的特点就是封装优雅、API友好。 Gin的一些特性: 快速 基于 Radix 树的路由,小内存占用。没有反射。可预测的 API 性能。 支持中间件 传入…...

dev c++调试录入数字后回车直接关闭

1、我的dev c版本是5.11 2、输入7后,回车就没有了,原因是1013,1.cpp未包含在项目中 3、新建项目,并将test_debug.cpp包含在项目内,就可以下断点调试了...

java期末复习

java jdk jre jvm .java-------->.class----jvm---->机器语言 编写源文件 编译源文件生成字节码 加载运行字节码 java语句执行顺序 顺序 选择 循环 异常处理 基本语法 方法格式 权限修饰符 返回值声明 方法名称(参数列表){方法中封装的逻辑功能;return 返回值;}–权…...

让新手变中手的ChatGPT 使用方法

让新手变中手的ChatGPT 使用方法 已经要一年了,没想到这篇还是一样有用。看到许多依然是一句话就要问出结果的教学,就知道AI 进步很快,但人的学习速度还需要更多时间。 以下为去年原版内容加上新的两招更新,希望帮助你使用ChatGPT 更好用,当未来文字可直接生成影像时,你…...

桥接模式类图与代码

欲开发一个绘图软件,要求使用不同的绘图程序绘制不同的图形。以绘制直线和圆形为例,对应的绘图程序如表 7.7 所示。 根据绘图软件的扩展性要求,该绘图软件将不断扩充新的图形和新的绘图程序。为了避免出现类爆炸的情况,现采用桥接…...

Oracle count的优化-避免全表扫描

Oracle count的优化-避免全表扫描 select count(*) from t1; 这句话比较简单,但很有玄机!对这句话运行的理解,反映了你对数据库的理解深度! 建立实验的大表他t1 SQL> conn scott/tiger 已连接。 SQL> drop table t1 purge…...

leetcode17. 电话号码的字母组合

题目描述: 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。 示例 1: 输入:digits "…...

如何设计与管理一个前端项目

目录 前端项目设计 前端项目搭建 洞察项目瓶颈 方案调研与选型对比 前端项目管理 合理的分工排期 风险把控 及时反馈与复盘 结束语 如果说基础知识的掌握是起跑线,那么使大家之间拉开差距的更多是前端项目开发经验和技能。对于一个项目来说,从框…...

【MQTT】mosquitto 的 “下载、交叉编译、使用” 详细教程,手把手搭建一个MQTT Broker

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀 🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C、数据结构、音视频🍭 ⏰发布时间⏰:2024-05-11 0…...

用云手机打造海外社媒矩阵

在全球经济一体化的大背景下,中国出海企业及B2B外贸公司正将海外社交媒体营销作为重要的市场拓展策略。为更好地触及不同受众群体,构建跨平台的社媒矩阵已成为企业营销的关键步骤。本文将探讨如何利用云手机技术,高效管理并运营多个海外社交媒…...

第 8 章 机器人平台设计之底盘实现(自学二刷笔记)

重要参考: 课程链接:https://www.bilibili.com/video/BV1Ci4y1L7ZZ 讲义链接:Introduction Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程 8.4.1 底盘实现_概述 1.ros_arduino_bridge 简介 该功能包包含Arduino库和用来控制Arduino的ROS驱动包&…...

✔ ★Java项目——设计一个消息队列(五)【虚拟主机设计】

虚拟主机设计 创建 VirtualHost实现构造⽅法和 getter创建交换机删除交换机创建队列删除队列创建绑定删除绑定发布消息 ★路由规则1) 实现 route ⽅法2) 实现 checkRoutingKeyValid3) 实现 checkBindingKeyValid4) 实现 routeTopic5) 匹配规则测试⽤例6) 测试 Router 订阅消息1…...

ntfs文件系统的优势 NTFS文件系统的特性有哪些 ntfs和fat32有什么区别 苹果电脑怎么管理硬盘

对于数码科技宅在新购得磁盘之后,出于某种原因会在新的磁盘安装操作系统。在安装操作系统时,首先要对磁盘进行分区和格式化,而在此过程中,操作者们需要选择文件系统。文件系统也决定了之后操作的流程程度,一般文件系统…...

Python Web框架Django项目开发实战:创建在线学习应用

注意:本文的下载教程,与以下文章的思路有相同点,也有不同点,最终目标只是让读者从多维度去熟练掌握本知识点。 下载教程:Python项目开发Django实战-创建在线学习应用-编程案例解析实例详解课程教程.pdf 在当今数字化教…...

用得助全媒体呼叫中心,让AI落到实处帮品牌做营销

怎么让人工智能落到实处的帮助到我们?我们今天来讲讲中关村科金得助全媒体呼叫中心是怎么让AI帮品牌。 这次聊的案例是知名的护肤品牌,该品牌在中国功能性护肤品市场占有率达到20.5%,这么高的市场占有率客户的咨询量也是非常庞大的&#xff0…...

【吃透Java手写】2-Spring(下)-AOP-事务及传播原理

【吃透Java手写】Spring(下)AOP-事务及传播原理 6 AOP模拟实现6.1 AOP工作流程6.2 定义dao接口与实现类6.3 初始化后逻辑6.4 原生Spring的方法6.4.1 实现类6.4.2 定义通知类,定义切入点表达式、配置切面6.4.3 在配置类中进行Spring注解包扫描…...

Spring原理分析--获取Environment资源对象

1.使用getEnvironment()获取环境信息 ApplicationContext接口继承了EnvironmentCapable接口,可以通过getEnvironment()获取Environment配置信息,例如: SpringBootApplication public class A01 {public static void main(String[] args) th…...

Android GPU渲染SurfaceFlinger合成RenderThread的dequeueBuffer/queueBuffer与fence机制(2)

Android GPU渲染SurfaceFlinger合成RenderThread的dequeueBuffer/queueBuffer与fence机制(2) 计算fps帧率 用 adb shell dumpsys SurfaceFlinger --list 查询当前的SurfaceView,然后有好多行,再把要查询的行内容完整的传给 ad…...

人民币数字和中文汉字转换

在PHP中,将人民币的中文汉字金额转换为数字,或者将数字转换为人民币的中文汉字金额,通常需要自定义一些函数来实现这一转换过程。下面分别给出这两个转换的示例代码。 数字转人民币中文汉字 function numberToChinese($num) { $cnNums arr…...

07_Flutter使用NestedScrollView+TabBarView滚动位置共享问题修复

07_Flutter使用NestedScrollViewTabBarView滚动位置共享问题修复 一.案发现场 可以看到,上图中三个列表的滑动位置共享了,滑动其中一个列表,会影响到另外两个,这显然不符合要求,先来看下布局,再说明产生这个…...

Java解决垂直鉴权问题(对垂直权限进行校验)

Java解决垂直鉴权问题(对垂直权限进行校验) 文章目录 Java解决垂直鉴权问题(对垂直权限进行校验)前言一、垂直鉴权是什么?二、实现过程1.新建接口权限菜单映射表2.项目初始化时加载接口菜单映射关系3.自定义过滤器拦截…...

【MySQL工具】pt-heartbeat

功能 pt-heartbeat - 监控 MySQL 复制延迟。 用法 pt-heartbeat [OPTIONS] [DSN] --update|--monitor|--check|--stop pt-heartbeat 用于测量 MySQL 或 PostgreSQL 服务器上的复制延迟。您可以使用它来更新主服务器或监控从服务器。如果可能,MySQL 连接选项将从您…...

实现vant的年月日时分秒组件

方法&#xff1a;van-datetime-picker&#xff08;type&#xff1a;datetime&#xff09;和 van-picker结合实现。 <template><div class"datetimesec-picker"><van-datetime-pickerref"timePickerRef"type"datetime" //年月日时…...

typescript 命名空间、装饰器

1、命名空间 命名空间&#xff1a;在代码量较大的情况下&#xff0c;为了避免各种变量命名的冲突&#xff0c;可将相似功能的函数、类、接口等放置到命名空间内。同Java的包.Net的命名空间一样&#xff0c;typescript 的命名空间可以将代码包裹起来&#xff0c;只对外暴露需要在…...

GPT问答SAP BW

以下回答由GPT-3.5回答,仅供参考. 这个AI工具超好用&#xff0c;每天都有免费额度&#xff0c;写文章、总结长视频、画图等&#xff0c;都几秒搞定&#xff01;快去下载Sider Chrome或Edge插件&#xff0c;薅羊毛&#xff01; https://sider.ai/invited?c43b289bf2616575daecf…...

使用zdppy_amauth开发激活用户接口

服务端代码&#xff1a; 1、创建数据库连接对象2、初始化数据库3、声明一个上下文4、挂载用户相关的路由&#xff0c;这里主要由 用户登录接口用户注册注册获取用户列表接口激活指定用户接口 5、启动服务 import mcrud import api import amauth import env import contextli…...

c++ memset 指针示例

目录 C 传一个float指针&#xff0c;在函数内部修改指针的值 c memset 指针示例 C 传一个float指针&#xff0c;在函数内部修改指针的值 #include <iostream>// 定义一个函数&#xff0c;它接受一个指向float的指针 void modifyValue(float* ptr) {// 通过解引用指针来…...

24考研双非上岸武汉理工大学电子信息专硕,855考研经验

目录 一、考研择校经验 二、武理考研初试经验 三、武理考研复试经验 一、考研择校经验 我建议学弟学妹们确定院校时没必要一上来就说我一定要考某个院校。其实考哪个学校是要在考研备考的过程中慢慢探索&#xff0c;慢慢研究的&#xff0c;不过最晚9月初一定要确定院校了&a…...

使用KubeKey 快速交付k8s v1.28.8集群

文章目录 服务器配置使用kubekey部署k8s1. 操作系统基础配置2. 安装部署 K8s2.1 下载 KubeKey2.2 创建 K8s 集群部署配置文件 3. 验证 K8s 集群3.1 验证集群状态 4. 部署测试资源5.验证服务 服务器配置 主机名IPCPU内存系统盘数据盘用途vm-16-11-ubuntu192.168.9.131128256Gi5…...