关于通过rabbitmq实现订单超时取消的两种方法

6,694 阅读4分钟

简介

传统中,商城取消订单采用定时器的方法查找超时未支付的订单并取消,这种方式在数据量大的时候,可能会影响性能,接下来我们通过中间件 rabbitmq 以两种方式来实现超时订单的数据

  • 发送 TLL 消息到死信队列,死信队列并不配置消费者,ttl 时间到期后,消息会从死信队列转发到另一个普通队列,从而被配置好的消费者消费
  • 延迟队列(需要安装插件 rabbitmq_delayed_message_exchange),直接从延迟队列设置消息的延迟时间,到期后消费者处理超时未支付订单

借助 RabbitMQ 死信队列实现

DLX,全称为 Dead-Letter-Exchange(死信交换机,和普通的交换机没区别),当一个消息在一个队列中变成死信,它能被发送到另一个交换机中,这个交换机就叫 DLX,绑定这个队列的队列就叫死信队列。

死信队列实现延迟消费流程

消息变成死信几种状况

  • 消息被拒绝

  • 消息过期

  • 队列达到最大长度

RabbitMQ 中可以在队列上、单条消息上设置 TTL。如果是设置在队列上,则可以认为该条队列中所有消息的 TTL 为设定值。

  • 队列 TTL 属性参数: x-message-ttl

  • 单条消息 TTL 参数: expiration

在死信队列配置了死信交换机、死信路由、TTL(进入死信队列中的消息可以存活的时间或者在发送消息时设置),由于没有配置消费者,时间到了消息会变成死信,从而进入配置好的中转站并被监听消费。

基本交换机、基本路由绑定死信队列(死信交换机+死信队列+TTL)

死信交换机绑定真正的消费队列

生产者 消费者

在生成一条订单的同时,并通过生产者发送到一条消息,消息过期从死信队列进入真正的队列并被消费者消费,判断订单是否还是待支付,是则取消订单

借助 RabbitMQ 延迟队列实现

延迟队列存储的对象是对应的延迟消息,所谓的"延迟消息"是指消息被发送以后,并不想消费者立刻拿到消息,而是等待特定时间后,消费者才能拿到这个消息进行消费

延迟队队列实现延迟消费流程

基本绑定-基本交换机+基本路由

消费者和生产者与死信队列实现的代码差不多,只不过在设置过期时间,在消费者发送时通过 messageProperties.setDelay(6000)设置

延迟队列的实现需要插件 rabbitmq_delayed_message_exchange 的支持,下载地址https://www.rabbitmq.com/community-plugins.html,要注意插件版本要和 rabbitmq 对应

使用 docker 安装 rabbitmq,并在其上安装延迟插件

  • 下载镜像文件 docker pull rabbitmq:management

  • 创建实例并启动 docker run -d --name rabbitmq --publish 5671:5671 --publish 5672:5672 --publish 4369:4369 --publish 25672:25672 --publish 15671:15671 --publish 15672:15672 rabbitmq:management

  • 复制下载好的插件到容器(rabbit)的/plugins目录下 docker cp rabbitmq_delayed_message_exchange-3.8.0.ez rabbit:/plugins

  • 进入容器内部 docker exec -it rabbit /bin/bash

  • 到插件目录下,启动插件 cd /plugins rabbitmq-plugins enable rabbitmq_delayed_message_exchange

  • 再重启下容器 docker restart rabbit

如果能在网页版http://ip:15672/(账号密码默认guest,guest)的Exchanges一栏下,在Add a new exchange 的 type 下拉框多了一个 x-delayed-message 的选择,则表示安装成功

代码的 github 地址 https://github.com/Ysomeone/miaosha-learn

总结

通过 rabbitmq 实现订单超时取消的两种方法,死信队列适合这种过期时间都是一致的,RabbitMQ 的队列是一个 FIFO 的有序队列,投入的消息都顺序的压进 MQ,RabbitMQ 也只会对队尾的消息进行超时判定,如果设置的过期的时间不一致,可能会导致过期的消息还没有被消费处理,即哪怕第二条在第 3 秒时就过期了,但由于第一条消息 5 秒过期,RabbitMQ 会等待到第一条被丢弃后,才对第二条进行判断,最终出现了第一条过期后第二条才跟着过期的结果。