likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 8

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

基于RabbitMQ实现

死信队列(Dead Letter Queue)

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

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

可以看到我们正常的队列,指定了死信交换机和路由Key RabbitMQ延时任务:订单超时取消

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

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

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

延时消息插件rabbitmq_delayed_message_exchange

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

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

首先我们要声明一个x-delayed-message类型的交换机 RabbitMQ延时任务:订单超时取消 RabbitMQ延时任务:订单超时取消

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

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

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

  • 消息丢失

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

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

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

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

转载自:https://juejin.cn/post/7323028184667717683
评论
请登录