订单处理延迟的原因有哪些?订单超时怎么处理我们用这种方案

人气:244 ℃/2024-12-22 21:38:01

简介: 描述业务常见的订单超时处理的几种方案及分析

作者 | 黄晓萌(学仁)

来源 | 阿里开发者公众号

背景

在企业的商业活动中,订单是指交易双方的产品或服务交易意向。交易下单负责创建这个交易双方的产品或服务交易意向,有了这个意向后,买方可以付款,卖方可以发货。

在电商场景下,买卖双方没有面对面交易,许多情况下需要通过超时处理自动关闭订单,下面是一个订单的流程:

如上图所示,一个订单流程中有许多环节要用到超时处理,包括但不限于:

1. JDK自带的延时队列

JDK中提供了一种延迟队列数据结构DelayQueue,其本质是封装了PriorityQueue,可以把元素进行排序。

  1. 把订单插入DelayQueue中,以超时时间作为排序条件,将订单按照超时时间从小到大排序。
  2. 起一个线程不停轮询队列的头部,如果订单的超时时间到了,就出队进行超时处理,并更新订单状态到数据库中。
  3. 为了防止机器重启导致内存中的DelayQueue数据丢失,每次机器启动的时候,需要从数据库中初始化未结束的订单,加入到DelayQueue中。
2. RabbitMQ的延时消息

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

RabbitMQ Delayed Message Plugin是官方提供的延时消息插件,虽然使用起来比较方便,但是不是高可用的,如果节点挂了会导致消息丢失。引用官网原文:

Delayed messages are stored in a Mnesia table (also see Limitations below) with a single disk replica on the current node. They will survive a node restart. While timer(s) that triggered scheduled delivery are not persisted, it will be re-initialised during plugin activation on node start. Obviously, only having one copy of a scheduled message in a cluster means that losing that node or disabling the plugin on it will lose the messages residing on that node.

消息的TTL 死信Exchange解决方案,先要了解两个概念:

一个消息被Consumer拒收了,并且reject方法的参数里requeue是false。也就是说不会被再次放在队列里,被其他消费者使用。

TTL到期的消息。

队列满了被丢弃的消息。

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

  1. 定义一个BizQueue,用来接收死信消息,并进行业务消费。
  2. 定义一个死信交换机(DLXExchange),绑定BizQueue,接收延时队列的消息,并转发给BizQueue。
  3. 定义一组延时队列DelayQueue_xx,分别配置不同的TTL,用来处理固定延时5s、10s、30s等延时等级,并绑定到DLXExchange。
  4. 定义DelayExchange,用来接收业务发过来的延时消息,并根据延时时间转发到不同的延时队列中。

不灵活,只能支持固定延时等级。

使用复杂,要配置一堆延时队列。

3. RocketMQ的定时消息

RocketMQ支持任意秒级的定时消息,如下图所示

使用门槛低,只需要在发送消息的时候设置延时时间即可,以java代码为例:

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的定时消息是如何实现的呢?

在RocketMQ中,使用了经典的时间轮算法。通过TimerWheel来描述时间轮不同的时刻,通过TimerLog来记录不同时刻的消息。

TimerWheel中的每一格代表着一个时刻,同时会有一个firstPos指向这个刻度下所有定时消息的首条TimerLog记录的地址,一个lastPos指向这个刻度下所有定时消息最后一条TimerLog的记录的地址。并且,对于所处于同一个刻度的的消息,其TimerLog会通过prevPos串联成一个链表。

当需要新增一条记录的时候,例如现在我们要新增一个 “1-4”。那么就将新记录的 prevPos 指向当前的 lastPos,即 “1-3”,然后修改 lastPos 指向 “1-4”。这样就将同一个刻度上面的 TimerLog 记录全都串起来了。

优点

缺点

点击查看原文,获取更多福利!

https://developer.aliyun.com/article/1161311?utm_content=g_1000368355

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

百科

More+
首页/电脑版/网名
© 2026 NiBaKu.Com All Rights Reserved.