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 允许通配符,一条消息可同时投递到多个队列。

相关推荐
beethobe13 小时前
PythonQt 学习之旅(一):从零构建 C++ 与 Python 的桥梁
c++·python·学习
如果你想拥有什么先让自己配得上拥有13 小时前
创业全周期证券学习法评价与系统观分析
学习
踏着七彩祥云的小丑13 小时前
嵌入式测试学习第 37 天:异常场景测试:断电、拔插、干扰、非法指令
单片机·嵌入式硬件·学习
Kobebryant-Manba14 小时前
学习门控循环单元gru
深度学习·学习·gru
风吹夏回14 小时前
RabbitMQ 核心术语 + Python pika 方法完整讲解
分布式·python·rabbitmq
Upsy-Daisy14 小时前
Hermes Agent 学习笔记 10:源码结构与整体架构总结,Hermes 到底是如何运转起来的?
笔记·学习·架构
你是个什么橙14 小时前
Python入门学习1:安装配置开发环境——Python或Annaconda,Pycharm
python·学习·pycharm
xxwl58514 小时前
Python语言初步认识(1)
开发语言·python·学习
风吹夏回14 小时前
RabbitMQ 三种模式入门:HelloWorld、WorkQueue、PubSub
分布式·rabbitmq·ruby
Niuguangshuo14 小时前
LangChain 学习之旅(五):Agent 与工具调用实战
学习·langchain