RabbitMQ延时任务:订单超时取消

在电商项目中,我们经常会遇到这样的需求:客户下单成功后,在一定的时间内未能按时(具体时间由业务规则决定)支付,需要将订单自动取消,释放占用的商品库存。类似于这样的需求(延时任务),我们该怎样解决呢?本文主要分享我项目中实际应用的方式

基于RabbitMQ实现

死信队列(Dead Letter Queue)

当消息在队列中变成死信(消费者无法正常处理的消息)之后,它会被重新投递到死信交换机,死信交换机上绑定的消费队列就是死信队列 RabbitMQ的 Queue 可以配置 x-dead-letter-exchangex-dead-letter-routing-key(可选)两个参数,用来控制队列内出现死信时,则按照这两个参数重新投递

可以看到我们正常的队列,指定了死信交换机和路由Key

核心思路:将设置了过期时间的消息,投放到没有消费者的队列,当消息过期后就会成为死信,死信消息会被投放到死信交换机,我们就可以通过死信队列来消费处理,从而达到延时任务的效果

但是这种方案只适用于过期时间统一的场景(不然实现起来会麻烦,也很复杂),如果过期时间不一样,那么就会出现过期时间长的消息阻塞过期时间短的消息(队列是先进先出的,每次只会判断队头的消息是否过期)。

延时消息插件rabbitmq_delayed_message_exchange

RabbitMQ可以不用死信队列也能实现延迟消息,那就是基于官方出的rabbitmq_delayed_message_exchange插件 (ps:怎么安装插件,网上有教程可以搜一下)

需要注意的是该插件支持的最大延长时间是(2^32)-1 毫秒,大约49天

首先我们要声明一个x-delayed-message类型的交换机

核心思路:通过这个交换机的消息不会立即进入到x-delayed-message队列中,而是存放到了一个基于Erlang开发的Mnesia数据库中,然后通过一个定时器去查询需要被投递的消息(判断消息过期时间),再把他们投递到x-delayed-message队列中

到这我们已经了解了RabbitMQ实现延时任务的核心,在实际的项目中,我们还需要一系列的手段来保证业务的正常进行

  • 消息丢失

通过任务表,来记录我们的延时任务

  • 消息重复投递或重复消费

任务表中我们记录了任务的执行状态,可以根据执行状态来避免此类问题

sql 复制代码
create table job_info
(
    id                 varchar(32)                        not null comment 'ID'
        primary key,
    job_no             varchar(64)                        not null comment '任务编号',
    tx_no              varchar(225)                       null comment '任务流水号',
    job_data           json                               null comment '任务数据',
    job_headers        varchar(512)                       null comment '任务数据头',
    job_execute_res    text                               null comment '任务执行结果',
    job_execute_date   datetime                           null comment '任务执行日期',
    job_status         varchar(16)                        null comment '任务状态',
    job_execute_status varchar(16)                        null comment '任务执行状态',
    call_url           varchar(255)                       null comment '执行调度地址',
    call_type          varchar(16)                        null comment '调度类型',
    call_retry         varchar(16)                        null comment '调度重试',
    remark             text                               null comment '备注',
    status             varchar(4)                         null
)
    comment '定时任务表' collate = utf8_bin;

总结

除了基于RabbitMQ实现延时任务,还有其它很多种包括像定时任务、JDK延迟队列、redis、RocketMQ等等来实现,至于选哪种方式,还是应该由具体的业务来决定(适合最重要)。不过如果遇到开头我说的场景,还是推荐基于RabbitMQ插件这种方式。

相关推荐
Pedantic1 小时前
SwiftUI 手势笔记
前端·后端
金銀銅鐵1 小时前
[Python] 从《千字文》中随机挑选汉字
后端·python
飘尘4 小时前
前端转型全栈(Java后端)的快速上手指引
前端·后端·全栈
浏览器工程师5 小时前
AI Agent 接浏览器任务,先别让它一路点到底
前端·后端
行者全栈架构师5 小时前
Maven dependency:tree 的 8 个高级用法
java·后端
Chenyiax5 小时前
从一次请求看懂 OkHttp:架构、调度与连接管理
后端
爱勇宝6 小时前
深扒 Anthropic 1680 位工程师简历:应届生几乎没机会,AI 公司最缺的不是博士
前端·后端·程序员
AskHarries6 小时前
工具失败时怎么办:重试、回滚、人工确认和风险提示
后端·程序员
苏三说技术8 小时前
Claude Code从失控到起飞,只用了这些技巧
后端