首页 > 其他分享 >订单超时处理

订单超时处理

时间:2023-04-23 16:35:36浏览次数:33  
标签:处理 数据库 订单 过期 定时 超时 消息

JDK自带的延时队列

  1. 把订单插入DelayQueue中,以超时时间作为排序条件,将订单按照超时时间从小到大排序。
  2. 起一个线程不停轮询队列的头部,如果订单的超时时间到了,就出队进行超时处理,并更新订单状态到数据库中。
  3. 为了防止机器重启导致内存中的DelayQueue数据丢失,每次机器启动的时候,需要从数据库中
  • 优点:简单,不需要第三方组件,成本低
  • 缺点:
    • 所有订单信息都要放入队列,内存占用大
    • 没办法分布式处理,效率低
    • 不适用订单量大的场景

RabbitMQ延时队列

RabbitMQ的延迟消息主要有两个解决方案

  1. RabbitMQ Delayed Message Plugin

    官方提供的延时消息插件,不是高可用的,节点挂了会丢失消息

  2. 消息TTL+死信Exchange

  • TTL:消息存活时间

    RabbitMQ可以对队列和消息分别设置TTL,如果对队列设置,则该队列中所有的消息都会具有相同的过期时间。如果超过了这个时间,我们就认为这个消息是死信。

  • 死信Exchange(DLX):死信交换机,在满足下面的条件时消息会进入该交换机

    • 一个消息被Consumer拒收,并且Reject方法的参数是false。也就是说不会被放入队列,被其他消费者重试
    • TTL到期的消息
    • 队列满了被丢弃的消息

一个延时消息的流程如下图

  • 优点:可以支持海量延时消息,支持分布式处理。

  • 缺点:

    • 不灵活,只能支持固定延时等级。
    • 使用复杂,要配置一堆延时队列

RocketMQ的定时消息

RocketMQ支持任意秒级的定时消息,只需要在发送消息的时候设置延时时间即可。

  • 优点:
    • 精度高,支持任意时刻(付费版)
    • 使用简单和普通消息一样
  • 缺点:
    • 使用限制,定时时长最大24小时。开源版本延迟时长固定18个级别
    • 使用成本高,每个订单都会生成一个定时消息,不会马上消费,占用MQ的存储
    • 同一时刻大量消息会导致消息延迟:定时消息的实现逻辑是通过时间轮算法,等定时时间到了以后才投递给消费者。如果同一时刻有大量消息需要处理,会照成系统压力过大,导致消息分发延迟,影响定时精度

Redis过期监听

  1. redis配置文件开启"notify-keyspace-events Ex"

  2. 监听key的过期回调事件

    var ret = ConnectionMultiplexer.Connect("127.0.0.1:6379,allowadmin=true");
    IDatabase database = ret.GetDatabase(0);
    ISubscriber subscriber = ret.GetSubscriber();
    subscriber.Subscribe("__keyevent@0__:expired", (channel, notificationType) =>
    {
    	Console.WriteLine(channel + "|" + notificationType);
    });
    Console.ReadKey();
    

使用Redis进行订单超时处理的流程图如下

这个方案表面看起来没问题,但是在实际生产上不推荐。

Redis主要使用定期删除和惰性删除两种策略来清理过期的key

  • 惰性删除:每次访问key的时候判断是否过期,如果过期就删除该key。若一个key过期了但是一直没有被访问,就会一直保存在数据库中
  • 定期删除:每隔一段时间(默认100ms)就随机抽取一些设置了过期时间的key,检查其是否过期,如果有过期就删除。之所以这么做,是为了通过限制删除操作的执行时长和频率来减少对CPU的影响。

activeExpireCycle函数每次运行时,都从一定数量的数据库中随机取出一定数量的键进行检查,并删除其中的过期键

从上面的过程中可以发现,Redis过期删除是不精准的。Redis真正发起过期通知不是在key过期的时候,而是在key被删除的时候。如果在Redis发起通知的时候,应用重启或者崩溃了,通知事件就有可能丢失了,导致订单一直无法关闭,有稳定性问题。如果一定要使用Redis过期监听方案,还是需要配合定时任务做补偿机制。

定时任务分布式批处理

通过定时任务不断轮询数据库订单,将超时的订单分配给不同机器分布式处理。

定时任务的优点:

  • 稳定性强:基于通知的方案(如MQ、Redis)需要考虑极端情况下的通知事件丢失的情况。定时任务只要保证业务幂等即可,哪怕这个批次有些订单没有处理或者处理过程中应用重启,下一个批次也可以继续处理,稳定性高。
  • 效率高:基于通知的方案需要一个订单一个定时消息,consumer消费者处理的时候也需要一个一个订单处理,对数据库TPS很高。使用定时任务可以批处理,每次取出一批数据,处理完成后批量更新数据库。
  • 可运维:基于数据库存储,可以很方便的对订单进行修改、暂停、删除等操作,如果业务执行失败也可以直接通过sql处理。
  • 成本低:相对于其他业务需要第三方组件,复用数据库的成本大大降低。

定时任务的天然缺点就是无法保证高精度。定时任务的延迟时间由执行周期决定,最大可能会有2倍执行周期的误差。如果执行频率很高,就会导致数据库QPS过高,数据库压力太大,影响正常业务。所以一般需要抽离出超时中心和超时库来单独做订单的超时调度。

总结

如果对于超时精度要求比较高,超时时间在24小时内且没有峰值压力的场景,推荐使用RocketMQ的定时消息为解决方案。

如果对于超时精度没那么敏感,并且有海量订单需要批处理,推荐使用基于定时任务的批处理方案。

PS:通过两种方式来判断订单是否关闭,首先判断状态字段,如果状态不是关闭的,再判断订单创建时间和当前时间之间的时间差,如果符合关闭时间,就返回订单已关闭。可以在下次被定时任务处理时,将状态设置为关闭。

标签:处理,数据库,订单,过期,定时,超时,消息
From: https://www.cnblogs.com/cplemom/p/17206372.html

相关文章

  • 【MAUI Blazor踩坑日记】1.关于图标的处理
    【MAUIBlazor踩坑日记】1.关于图标的处理前言本系列文章,默认你已经踏上了MAUIBlazor的贼船,并且对MAUIBlazor有了一些了解,知道MAUI是什么,知道Blazor是什么。不会教你怎么写MAUIBlazor的项目,只是提出一些注意点,避免踩坑。正文先看官方资料https://learn.microsoft.com/z......
  • 处理U8 结转损益凭证存在银行两清标志/分录 不能作废凭证SQL操作
    --处理U8结转损益凭证存在银行两清标志/分录不能作废凭证SQL操作:---查询凭证号及会计期间select*fromGL_accvouchwhereino_id=98andiyperiod='202101'---更新凭证两清标记为空updateGL_accvouchsetiflagPerson=nullwhereino_id=98andiyperiod='202101......
  • 盘点一个列表处理基础问题(四种方法),最后两种最惊艳!
    今日鸡汤渡远荆门外,来从楚国游。大家好,我是Python进阶者。本文赠送《轻松学Python》纸质书,活动参与方式见文末。一、前言昨天在Python最强王者群有个叫【老松鼠】的粉丝问了一道关于列表处理的问题,如下图所示。原文本如下所示:[('a',1),('a',2),('a',3),('b',1),('b',2),......
  • 从数据库查询权限信息、自定义失败处理
    从数据库查询权限信息我们只需要根据用户id去查询到其所对应的权限信息即可。所以我们可以先定义个mapper,其中提供一个方法可以根据userid查询权限信息。MenuMapper持久层接口publicinterfaceMenuMapperextendsBaseMapper<Menu>{List<String>selectPermsByUser......
  • Go语言入门10(异常处理)
    异常处理panic异常处理​ 如果出现了panic异常,那么会停止当前函数的运行,然后会找recover()方法,如果没有的话,就会报错退出程序,如果有就会执行recover的方法体中的方法​ 我们可以使用defer延迟处理函数来捕获panic异常,用recover()来从错误场景中恢复,必须的在defer修饰的方法中......
  • Python_9 py文件导入和路径处理
    一、查缺补漏Python中两个值交换可以直接交换如:a,b=b,a冒泡就是从小到大排序,因为越到后越大自动导包也适用于自己创建的模块关于正斜杠和反斜杠https://www.cnblogs.com/yangjian319/p/4801675.html 5. 内置异常汇总https://www.cnblogs.com/nmb-musen/p/108......
  • java基础-异常处理(六)
    异常处理,出现的问题有很多种,比如角标越界,空指针等都是。就对这些问题进行分类。而且这些问题都有共性内容比如:每一个问题都有名称,同时还有问题描述的信息,问题出现的位置,所以可以不断的向上抽取。形成了异常体系。--------java.lang.Throwable:Throwable:可抛出的。   |--Error:错......
  • jmeter之仅一次控制器、If控制器、前置处理器beanshell、后置处理器beanshell
    1、仅一次控制器作用是把请求放到仅一次里面的时候,不管循环数写几次,放到仅一次控制器里的请求都只跑一次,大部分用在登录,一般就登录一次就可以了,还有jdbc连接也是连接一次就可以了,不用重复连接。2、If控制器如果(If)控制器,if控制器如果里面的条件不满足,是不执行里面的请求的,看用户自......
  • odoo中打印模板处理selection字段
    在odoo中处理selection字段,通常情况下,如果拿到的是key,还要再翻译成value.这里可以用fields_get()这个函数,来实现这个问题${dict(item.fields_get(allfields=['type_id'])['type_id']['selection']).get(item.type_id)} 这里的item是数据集‘type_id’selection的字段......
  • 关于ssh密码忘记的处理
    01.任意文件位置运行 GitBashHere02.切换到ssh目录:  cd~/.ssh03.查看ssh内容:如果之前已经生成过ssh公钥,则存在三个文件04.删除所有SSH 相关内容: rm-rf~/.ssh/*此时再用 ls指令,已经查不到任何内容了05.创建新的ssh:  ssh-keygen-t......