rabbitmq学习demo,包含普通消息,TTL+死信队列,topic交换机三种情况,以项目形式讲解

mall-demo-mq

RabbitMQ 学习 Demo,通过 REST 接口手动触发不同 MQ 场景,观察控制台日志。项目源码在项目地址

项目结构

复制代码
mall-demo-mq
├── pom.xml
├── README-MQ.md
└── src/main
    ├── java/com/mtcarpenter/mall/demo/mq
    │   ├── MallDemoMqApplication.java        # 启动类
    │   ├── component
    │   │   └── RabbitMqDemoListener.java     # 消费者监听器
    │   ├── config
    │   │   └── RabbitMqDemoConfig.java       # 交换机/队列/绑定/RabbitTemplate 配置
    │   ├── constant
    │   │   └── MqDemoConstants.java          # 交换机、队列、路由键常量
    │   ├── controller
    │   │   └── DemoMqController.java         # REST 入口,触发发送消息/创建订单
    │   ├── domain
    │   │   ├── DemoOrder.java                # 模拟订单实体
    │   │   ├── DemoOrderStatus.java          # 订单状态枚举
    │   │   └── MqDemoMessage.java            # 通用消息体
    │   └── service
    │       ├── DemoOrderService.java         # 模拟订单服务(内存 Map)
    │       └── RabbitMqDemoService.java      # 生产者服务,封装消息发送逻辑
    └── resources
        ├── application.yml                   # 端口、RabbitMQ 连接、手动 ack 等配置
        └── static
            └── demo-mq-page.html             # 浏览器交互页面

文件说明

文件 说明
pom.xml 项目依赖声明,parent 为 spring-boot-starter-parent,依赖 spring-boot-starter-webspring-boot-starter-amqp
MallDemoMqApplication.java Spring Boot 启动类,@SpringBootApplication
RabbitMqDemoListener.java 消费者,包含 4 个 @RabbitListener 方法,分别监听普通队列、订单取消队列、Topic 队列 A/B/C
RabbitMqDemoConfig.java 声明所有交换机、队列、绑定关系;配置 RabbitTemplate 的 JSON 转换器、ConfirmCallback、ReturnCallback
MqDemoConstants.java 集中定义交换机名、队列名、路由键名,方便查阅完整链路
DemoMqController.java REST 接口入口,提供发送普通消息、无法路由消息、创建/支付/查询订单、发送 Topic 消息等接口
DemoOrder.java 模拟订单 POJO,包含 orderId、status、timeoutSeconds、createTime、payTime、cancelTime
DemoOrderStatus.java 订单状态枚举:WAIT_PAYPAIDCANCELED
MqDemoMessage.java 通用消息体,包含 messageId、messageType、businessId、content、createdAt
DemoOrderService.java 内存订单服务,用 ConcurrentHashMap 存储订单,提供创建、支付、超时取消、查询操作
RabbitMqDemoService.java 生产者服务,封装 4 种发送方法:普通 direct、无法路由、订单延迟、Topic
application.yml 端口 8088,RabbitMQ 连接本机,开启 publisher-confirms/returns,手动 ack,prefetch=1
demo-mq-page.html 浏览器交互页面,点击按钮即可触发各接口,右侧显示响应结果

链路讲解

一、普通消息链路(Direct Exchange)

最基础的生产者 → 交换机 → 队列 → 消费者流程。

复制代码
生产者                          RabbitMQ                              消费者
  │                               │                                    │
  │  convertAndSend(              │                                    │
  │    demo.direct.exchange,      │                                    │
  │    demo.direct,               │                                    │
  │    message)                   │                                    │
  │ ────────────────────────────► │                                    │
  │                               │  routing key = demo.direct         │
  │                               │  匹配绑定关系                       │
  │                               │ ──────────────────────────────────► │
  │                               │                                    │
  │  ◄── ConfirmCallback(ack=true)│                         basicAck() │
  │                               │  ◄───────────────────────────────── │

关键资源:

类型 名称 说明
Exchange demo.direct.exchange Direct 类型,精确匹配 routing key
Queue demo.direct.queue 普通队列,无死信配置
Binding routing key = demo.direct 交换机与队列的绑定关系

流程步骤:

  1. Controller 调用 RabbitMqDemoService.sendDirectMessage()
  2. RabbitTemplate.convertAndSend() 将消息发到 demo.direct.exchange,routing key 为 demo.direct
  3. 交换机根据 routing key 找到绑定队列 demo.direct.queue,投递消息
  4. ConfirmCallback 回调,ack=true 表示消息已到达交换机
  5. RabbitMqDemoListener.handleDirectMessage() 消费消息,手动 basicAck

无法路由场景: 使用 routing key demo.no.queue 发送,交换机能收到(ConfirmCallback ack=true),但找不到队列,触发 ReturnCallback

易混洗点

Direct 交换机不局限于和队列一对一映射,核心匹配规则是消息的路由键必须与队列的绑定键完全相等。交换机承担消息中转的作用,只要存在匹配绑定关系的队列,就会将消息路由至对应队列。具体特性如下:

  1. 多个队列绑定相同绑定键时,这些队列都会接收到同一条消息;
  2. 一台 Direct 交换机可使用不同绑定键,分别绑定多个队列;
  3. 单个队列也可配置多个绑定键,以此接收不同路由键投递的消息。

二、TTL + 死信队列链路(订单超时取消)

利用消息 TTL 过期后自动转发到死信交换机,实现延迟消费。

复制代码
生产者                        RabbitMQ                                    消费者
  │                             │                                          │
  │  convertAndSend(            │                                          │
  │    demo.order.delay.exchange│                                          │
  │    demo.order.delay,        │                                          │
  │    message + TTL)           │                                          │
  │ ──────────────────────────► │                                          │
  │                             │  消息进入 demo.order.delay.queue          │
  │                             │  ── 等待 TTL 到期 ──►                     │
  │                             │                                          │
  │                             │  TTL 过期,消息变为死信                     │
  │                             │  死信转发到 demo.order.dead.exchange       │
  │                             │  routing key = demo.order.cancel          │
  │                             │  投递到 demo.order.cancel.queue           │
  │                             │ ────────────────────────────────────────► │
  │                             │                               查询订单状态 │
  │                             │                     WAIT_PAY → CANCELED   │
  │                             │                         PAID → 跳过取消   │
  │                             │                               basicAck()  │
  │                             │  ◄──────────────────────────────────────── │

关键资源:

类型 名称 说明
Exchange demo.order.delay.exchange 延迟交换机,Direct 类型
Queue demo.order.delay.queue 延迟队列,配置了 x-dead-letter-exchangex-dead-letter-routing-key无消费者监听
Exchange demo.order.dead.exchange 死信交换机,接收过期消息
Queue demo.order.cancel.queue 订单取消队列,消费者监听此队列
Binding delay 队列 → delay 交换机,routing key = demo.order.delay 生产者投递入口
Binding cancel 队列 → dead 交换机,routing key = demo.order.cancel 死信投递出口

流程步骤:

  1. DemoOrderService.createOrder() 创建订单(状态 WAIT_PAY),调用 RabbitMqDemoService.sendOrderTimeoutMessage()
  2. 消息设置 expiration(毫秒级 TTL),发送到 demo.order.delay.exchange
  3. 消息进入 demo.order.delay.queue没有消费者监听,消息在此等待
  4. TTL 到期,RabbitMQ 将消息作为死信,按队列配置的 x-dead-letter-exchange 转发到 demo.order.dead.exchange
  5. 死信交换机按 x-dead-letter-routing-key = demo.order.cancel 投递到 demo.order.cancel.queue
  6. RabbitMqDemoListener.handleOrderTimeoutMessage() 消费消息,调用 DemoOrderService.cancelOrderIfStillWaitingPay()
  7. 幂等判断 :只有 WAIT_PAY 状态才取消;PAID 跳过;CANCELED 跳过

核心思想: 延迟队列只是"等待室",订单取消队列才是"处理室"。延迟消息只是提醒"该检查了",不能无脑取消。


三、Topic 交换机链路

Topic 交换机支持 routing key 的模式匹配:* 匹配一个单词,# 匹配零个或多个单词。

复制代码
生产者                          RabbitMQ                              消费者
  │                               │                                    │
  │  convertAndSend(              │                                    │
  │    demo.topic.exchange,       │                                    │
  │    topic.user.create,         │                                    │
  │    message)                   │                                    │
  │ ────────────────────────────► │                                    │
  │                               │  topic.user.create 匹配结果:        │
  │                               │  ┌─ topic.# ──► queue.a ──────────► │ 队列 A 消费者
  │                               │  └─ topic.user.* ──► queue.b ─────► │ 队列 B 消费者
  │                               │                                    │
  │                               │  topic.order.create 匹配结果:      │
  │                               │  ┌─ topic.# ──► queue.a ──────────► │ 队列 A 消费者
  │                               │  └─ topic.order.create ──► queue.c ► │ 队列 C 消费者
  │                               │                                    │
  │                               │  topic.any.thing 匹配结果:         │
  │                               │  └─ topic.# ──► queue.a ──────────► │ 队列 A 消费者

关键资源:

类型 名称 绑定模式 说明
Exchange demo.topic.exchange --- Topic 类型交换机
Queue demo.topic.queue.a topic.# 匹配所有以 topic. 开头的路由键
Queue demo.topic.queue.b topic.user.* 只匹配 topic.user. 后跟一个单词
Queue demo.topic.queue.c topic.order.create 精确匹配,效果等同于 Direct

路由匹配示例:

routing key 队列 A (topic.#) 队列 B (topic.user.*) 队列 C (topic.order.create)
topic.user.create
topic.user.delete
topic.order.create
topic.any.thing
topic.user.a.b ❌(* 只匹配一个单词)

流程步骤:

  1. Controller 调用 RabbitMqDemoService.sendTopicMessage(routingKey, content)
  2. 消息发送到 demo.topic.exchange,携带用户指定的 routing key
  3. 交换机根据绑定模式逐一匹配,将消息投递到所有匹配的队列
  4. 各队列对应的 @RabbitListener 方法分别消费,手动 basicAck

与 Direct 的区别: Direct 要求 routing key 完全相等;Topic 允许通配符,一条消息可同时投递到多个队列。

相关推荐
星夜夏空991 小时前
STM32单片机学习(34) —— ADC实验: ADC规则组配合DMA实现自动化转运
stm32·单片机·学习
Realdagongzai1 小时前
Linux 6.19.10 内核调度器算法详解
linux·学习·算法·spring·kernel
xxl大卡2 小时前
Redis完整详细学习笔记
redis·笔记·学习
星夜夏空992 小时前
FreeRTOS学习(1)——裸机开发与操作系统
单片机·嵌入式硬件·学习
Cat_Rocky2 小时前
CICD-Git简单学习 操作流程后续补
git·学习
weixin_550083152 小时前
基于知识图谱的python个性化学习路径推荐系统项目源码
人工智能·学习·知识图谱
贺国亚2 小时前
分布式并发
分布式·wpf
魔法阵维护师2 小时前
从零开发游戏需要学习的c#模块,第二十七章(远程攻击 —— 发射子弹)
学习·游戏·c#
一口吃俩胖子2 小时前
【脉宽调制DCDC功率变换学习笔记022】DCDC变换器的稳定性、奈奎斯特准则、增益裕度和相位裕度
笔记·学习