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插件这种方式。

相关推荐
追逐时光者4 小时前
推荐 12 款开源美观、简单易用的 WPF UI 控件库,让 WPF 应用界面焕然一新!
后端·.net
Jagger_4 小时前
敏捷开发流程-精简版
前端·后端
苏打水com5 小时前
数据库进阶实战:从性能优化到分布式架构的核心突破
数据库·后端
间彧6 小时前
Spring Cloud Gateway与Kong或Nginx等API网关相比有哪些优劣势?
后端
间彧6 小时前
如何基于Spring Cloud Gateway实现灰度发布的具体配置示例?
后端
间彧6 小时前
在实际项目中如何设计一个高可用的Spring Cloud Gateway集群?
后端
间彧6 小时前
如何为Spring Cloud Gateway配置具体的负载均衡策略?
后端
间彧6 小时前
Spring Cloud Gateway详解与应用实战
后端
EnCi Zheng7 小时前
SpringBoot 配置文件完全指南-从入门到精通
java·spring boot·后端
烙印6017 小时前
Spring容器的心脏:深度解析refresh()方法(上)
java·后端·spring