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

news2024/11/25 23:03:11

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、兜底意识 + 配置监控

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1648824.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DAPP开发:揭秘DAPP软件开发的秘密

随着区块链技术的飞速发展,DAPP(去中心化应用)的开发逐渐成为了一个热门话题。在本文中,我们将探讨如何从零开始开发DAPP软件,并深入思考DAPP开发中的关键问题。 一、了解DAPP开发的基础知识 在开始开发DAPP之前&…

Web3:下一代互联网的科技进化

随着科技的不断演进,互联网已经成为了我们生活中不可或缺的一部分。而在Web3时代,我们将会见证互联网进化的下一个阶段。本文将探讨Web3作为下一代互联网的科技进化,以及它所带来的重要变革和影响。 传统互联网的局限性 传统互联网存在诸多…

绘画作品3d数字云展厅提升大众的艺术鉴赏和欣赏能力

3D虚拟展厅作为未来艺术的展示途径,正逐渐成为文化创意产业蓬勃发展的重要引擎。这一创新形式不仅打破了传统艺术展览的局限性,更以其独特的魅力吸引着全球观众的目光。 3D虚拟艺术品展厅以其独特的魅力,助力提升大众的艺术鉴赏和欣赏能力。观…

开发组合php+mysql 人才招聘小程序源码搭建 招聘平台系统源码+详细图文搭建部署教程

随着互联网的快速发展,传统的招聘方式已经不能满足企业和求职者的需求。为了提高招聘效率,降低招聘成本,越来越多的人开始关注人才招聘小程序、在线招聘平台。分享一个人才招聘小程序源码及搭建,让招聘更加高效便捷。系统是运营级…

Android getevent命令详细分析

在调试Android 的输入事件时,经常使用 “getevent -lrt” 命令,来确认驱动上报数据是否正常。从源码的角度来详细的分析一下getevent 这个程序。 首先用ls命令来看一下getevent lrwxr-xr-x 1 root shell 7 2023-11-20 10:08 system/bin/getevent -> …

视频号小店是普通人的机会吗?看完你就明白了!

大家好,我是电商小V 视频号小店是普通人的机会吗?我可以很确定的说:视频号小店就是普通人的机会,并且是很大的机会, 首先就是视频号小店这个项目还没有自然流量的入口,是一个还没有完全开放私域电商的平台&…

python+flask+ldap3搭建简易版IDaaS系统(前端站点)

Python工具开源专栏 Py0006 pythonflaskldap3搭建简易版IDaaS系统(前端站点) Python工具开源专栏前言目录结构前端网站的部分演示首页查询数据数据同步数据关联查询系统日志 完整代码已在GitHub上开源 前言 pythonflaskldap3搭建简易版IDaaS系统的前端站…

XSS漏洞---XSS-labs通关教程

文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 Level-1 过滤源码&#xff1a;无 pyload&#xff1a; name<script>alert(1)</script> Level-2 过滤源码&#xff1a;利用转译函数将特殊字符转译为实体字符 $str $_GET["…

RNN 及其变体的数学理解

目录 一、介绍&#xff1a; 二、什么是 RNN 以及它与前馈神经网络有何不同&#xff1a; 三、RNN 背后的数学 3.1 RNN的数学方程 3.2 参数和梯度 3.2.1 损耗相对于V的梯度 3.2.2 损失相对于W的梯度 四、RNN 变体&#xff08;LSTM 和 GRU&#xff09; 4.1 LSTM&#xff08;长短期…

代码随想录算法训练营第36期DAY19

DAY19 104二叉树的最大深度 根节点的高度就是最大深度。 非递归法&#xff1a; /** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) …

Ansible自动运维工具之playbook

一.inventory主机清单 1.定义 Inventory支持对主机进行分组&#xff0c;每个组内可以定义多个主机&#xff0c;每个主机都可以定义在任何一个或多个主机组内。 2.变量 &#xff08;1&#xff09;主机变量 [webservers] 192.168.10.14 ansible_port22 ansible_userroot ans…

央国企加速新质生产力形成和发展,HR数字化工具如何推动创新内核构建?

自今年两会以来&#xff0c;“新质生产力”一词获得了广泛的关注。众多专家学者对其重要性、定义及作用进行了热烈且深入的讨论&#xff0c;一致强调了新质生产力的核心地位。对于那些致力于转型为现代化国有企业的国资中央企业而言&#xff0c;培育新质生产力无疑成为了当前及…

JVM笔记-常用命令

1、jstat jstat是一个极强的监视JVM的工具&#xff0c;可以用来监视JVM的各种堆和非堆的大小以及内存使用量。 Usage: jstat -help|-optionsjstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]jstat的常用用法如图所示&#xff…

Java Swing手搓童年坦克大战游戏(III)

坦克大战豪华山寨版二期工程 计划&#xff1a;实现【道具功能】【分数统计、排行榜】【多种类型敌军坦克派遣】【自建地图】【游戏存档读档】【联网实现双人配合】等&#xff0c;修复一些严重的bug。由于功能比较多&#xff0c;目测会分多篇文章记录…… 前言 通过对原游戏的…

C语言 函数概述

好 接下来 我们来讲函数 构建C程序的最佳方式 就是模块化程序设计 C语言中 最基本的程序模块被称为 函数 所以 这个知识点的重要性不言而喻 这里 我们讲个故事 诸葛亮六出祁山时 为了逼司马懿出战 派人送给力司马懿一件女人衣服 司马懿只是为使者 诸葛亮的饮食起居 使者感叹…

光电探测器性能指标测试

光电探测器的三个核心指标&#xff1a; 带宽&#xff0c;转换增益&#xff0c;噪声(信噪比&#xff0c;NEP&#xff0c;噪声密度) 测试环境&#xff1a;可调谐激光器&#xff08;CW LASER&#xff09;&#xff0c;强度调制器(AM)&#xff0c;信号发生器(AWG)&#xff0c;可调衰…

stm32f103zet6_DAC_2_输出电压

实现效果 DAC输出的电压 同过电压表测量电压 1.DAC配置的步骤 初始化DAC时钟。配置DAC的GPIO端口。设置DAC的工作模式&#xff08;例如&#xff0c;是否使用触发功能&#xff0c;是否启用DAC中断等&#xff09;。启动DAC。 2常用的函数 函数 HAL_DAC_Start() - 开启指定…

5月6(信息差)

&#x1f30d;一次预测多个token&#xff0c;Meta新模型推理加速3倍&#xff0c;编程任务提高17% https://hub.baai.ac.cn/view/36857 &#x1f384; LeetCode 周赛超越 80% 人类选手&#xff0c;推理性能超 Llama3-70B。 ✨ 我国量子计算机实现“四算合一” 实现通算、…

CAPL如何实现TLS握手认证

CAPL有专门的章节介绍如何实现TLS握手认证的函数: CAPL调用哪些函数实现TLS握手认证,需要了解TLS在整个通信过程的哪个阶段。 首先TCP需要建立连接,这是TLS握手的前提。当TLS握手认证完成后,可以传输数据。 所以TLS握手开始前需要确保TCP建立连接,TCP传输数据前需要确保…

5.6代码

1.最大公约数 这个题最重要的是要找到一个区间是1&#xff0c;找到之后就可以直接加次数就可以了 #include <bits/stdc.h>using namespace std;main() {long long n,i,j,a0,b,ans99999;cin>>n;long long s[n],dp[n][n];for(i0;i<n;i){cin>>s[i];if(s[i]1…