黑马商城学习项目10-SpringAMQP
SpringAMQP
在实际业务开发中,我们不会通过 RabbitMQ 控制台手动收发消息,而是基于编程实现。由于 RabbitMQ 遵循 AMQP 协议,具备跨语言特性,但官方 Java 客户端编码复杂,因此生产环境中常结合 Spring 生态使用 ——Spring 官方提供的 SpringAMQP 框架,基于 RabbitMQ 封装了消息收发模板,且支持 SpringBoot 自动装配,大幅简化开发。
SpringAMQP 官方地址:https://spring.io/projects/spring-amqp
SpringAMQP 核心提供三大功能:
- 自动声明队列、交换机及绑定关系(无需手动在控制台创建)
- 基于注解的监听器模式,异步接收消息(无需手动编写监听逻辑)
- 封装
RabbitTemplate工具类,简化消息发送(无需处理底层连接、序列化等细节)
本章将详细讲解如何通过 SpringAMQP 实现 RabbitMQ 的消息收发,覆盖核心模型与实际业务改造。
1.1 导入 Demo 工程
课前资料提供了用于学习 SpringAMQP 的 Demo 工程,建议按以下步骤导入:
- 将工程压缩包解压到本地工作空间(如
D:\workspace) - 打开 IDEA,通过「File -> Open」选择解压后的工程根目录,完成导入
1.1.1 工程结构说明
导入后的工程包含 3 个模块,职责分工明确:
- mq-demo:父工程,统一管理项目依赖(如 SpringAMQP、Lombok、单元测试等)
- publisher:消息生产者模块,负责发送消息到 RabbitMQ
- consumer:消息消费者模块,负责监听 RabbitMQ 队列并处理消息
1.1.2 核心依赖配置
父工程 mq-demo 的 pom.xml 已预先配置 SpringAMQP 相关依赖,子模块可直接继承使用,无需重复引入。核心依赖如下:
1 |
|
1.2 快速入门(简单队列模型)
实际开发中消息通常经交换机路由到队列,但为了快速上手,入门案例将跳过交换机,直接向队列发送消息(此模式仅用于测试,生产环境极少使用)。
核心流程:
- 生产者(publisher)直接发送消息到指定队列
- 消费者(consumer)监听该队列,异步接收并处理消息
1.2.1 步骤 1:手动创建队列(控制台操作)
首先在 RabbitMQ 管理控制台创建测试队列,操作如下:
- 访问 RabbitMQ 控制台(默认地址:http:// 虚拟机 IP:15672,如
http://192.168.150.101:15672) - 输入用户名(如
hmall)和密码(如123)登录,进入「Queues」页面 - 点击「Add a new queue」,输入队列名
simple.queue,其他参数(如 Durability、Auto delete)保持默认,点击「Add queue」完成创建 - 刷新页面,可在队列列表中看到
simple.queue,状态为「Ready: 0」(暂无消息)
1.2.2 步骤 2:生产者发送消息(publisher 模块)
1.2.2.1 配置 MQ 连接信息
在 publisher 模块的 src/main/resources/application.yml 中,添加 RabbitMQ 连接配置(需替换为你的虚拟机 IP 和账号密码):
1 | spring: |
1.2.2.2 编写消息发送测试类
在 publisher 模块的 src/test/java/com/itheima/publisher/amqp 目录下,创建 SpringAmqpTest 类,使用 RabbitTemplate 发送消息:
1 | package com.itheima.publisher.amqp; |
1.2.2.3 执行测试
右键运行 testSimpleQueue 方法,执行成功后,回到 RabbitMQ 控制台的「Queues」页面:
- 刷新
simple.queue的详情,可看到「Ready: 1」(1 条待消费消息) - 点击队列名进入详情页,在「Get messages」区域点击「Get Message (s)」,可查看消息内容为
hello, spring amqp!
1.2.3 步骤 3:消费者接收消息(consumer 模块)
1.2.3.1 配置 MQ 连接信息
与生产者配置一致,在 consumer 模块的 src/main/resources/application.yml 中添加 RabbitMQ 连接:
1 | spring: |
1.2.3.2 编写消息监听类
在 consumer 模块的 src/main/java/com/itheima/consumer/listener 目录下,创建 SpringRabbitListener 类,通过 @RabbitListener 注解监听队列:
1 | package com.itheima.consumer.listener; |
1.2.3.3 测试消息接收
启动
consumer模块的主类(如ConsumerApplication)回到
publisher模块,再次运行testSimpleQueue方法发送消息查看
consumer模块的控制台输出,会看到:1
spring 消费者接收到消息:【hello, spring amqp!】
同时 RabbitMQ 控制台的
simple.queue队列「Ready」数量变为 0(消息已被消费)
1.3 WorkQueues 模型(工作队列模型)
当消息处理耗时较长(如生成报表、调用第三方接口),生产者发送消息的速度可能远快于消费者处理速度,导致消息堆积。此时可使用WorkQueues 模型:多个消费者绑定到同一个队列,共同分担消息处理任务,提升整体处理效率。
1.3.1 核心原理
- 队列中的消息会被均匀分配给多个消费者(默认策略),但可通过配置实现「能者多劳」
- 同一条消息仅会被一个消费者处理(避免重复处理)
- 适用于「任务拆分」场景,如电商订单异步处理、日志批量消费等
1.3.2 步骤 1:创建工作队列(控制台操作)
参考 1.2.1 的步骤,在 RabbitMQ 控制台创建队列 work.queue(用于模拟消息堆积场景)。
1.3.3 步骤 2:生产者发送大量消息(模拟堆积)
在 publisher 模块的 SpringAmqpTest 类中,添加循环发送消息的测试方法,模拟每秒发送 50 条消息(远超单个消费者处理能力):
1 | /** |
1.3.4 步骤 3:多个消费者接收消息(默认均匀分配)
在 consumer 模块的 SpringRabbitListener 类中,添加 2 个监听方法(模拟两个处理速度不同的消费者),注意通过 Thread.sleep 模拟处理耗时:
1 | import java.time.LocalTime; |
1.3.5 步骤 4:测试默认分配策略
- 启动
consumer模块(两个消费者同时启动) - 运行
publisher模块的testWorkQueue方法发送 50 条消息 - 查看
consumer控制台输出,会发现:- 消费者 1 和消费者 2 各处理 25 条消息(默认均匀分配)
- 消费者 1 很快处理完 25 条,进入空闲状态
- 消费者 2 需要长时间处理 25 条,导致整体任务耗时过长(约 5 秒)
问题分析:默认策略按「消息数量」分配,不考虑消费者处理能力,导致资源浪费(快的空闲、慢的繁忙),无法解决消息堆积问题。
1.3.6 步骤 5:配置「能者多劳」(优化分配策略)
通过 prefetch 参数控制消费者「预取消息数量」,设置为 1 时,消费者必须处理完当前消息后,才能获取下一条消息,实现「能者多劳」。
修改 consumer 模块的 application.yml,添加如下配置:
1 | spring: |
1.3.7 步骤 6:重新测试优化效果
- 重启
consumer模块(使prefetch配置生效) - 再次运行
publisher的testWorkQueue方法发送 50 条消息 - 查看控制台输出,会发现:
- 消费者 1(快)处理了约 44 条消息
- 消费者 2(慢)仅处理了约 6 条消息
- 整体任务耗时缩短至 1 秒左右(充分利用快消费者的能力)
举例:比如快递站有 2 个快递员,A 每小时能送 50 件,B 每小时能送 5 件。默认策略给 A、B 各 25 件,A1 小时完成后闲置,B 需要 5 小时;优化后 A 多送、B 少送,整体 2 小时内完成,效率大幅提升。
1.3.8 WorkQueues 模型总结
- 适用场景:消息处理耗时较长,需要多个消费者分担任务
- 核心配置:
prefetch: 1实现「能者多劳」,避免资源浪费 - 关键特性:同一个队列的消息仅被一个消费者处理,保证消息不重复消费
1.4 交换机类型(核心概念)
前面的案例跳过了交换机,但生产环境中消息必须通过交换机路由到队列。交换机本身不存储消息,仅负责按规则转发,若没有匹配的队列,消息会直接丢失。
RabbitMQ 支持 4 种交换机类型,常用前 3 种:
| 交换机类型 | 核心特点 | 适用场景 |
|---|---|---|
| Fanout | 广播模式:将消息转发给所有绑定的队列 | 全量通知(如系统公告、状态同步) |
| Direct | 精准路由:按「RoutingKey 完全匹配」转发消息 | 单目标通知(如订单状态变更仅通知订单服务) |
| Topic | 通配符路由:按「RoutingKey 通配符匹配」转发 | 多目标筛选通知(如「中国新闻」「日本新闻」分类转发) |
| Headers | 头匹配:按消息头属性匹配,不依赖 RoutingKey | 复杂属性筛选(极少使用) |
1.5 Fanout 交换机(广播模式)
Fanout 交换机(也称「扇出交换机」)会将消息转发给所有绑定它的队列,无论 RoutingKey 是什么(甚至可以不指定 RoutingKey),适合「全量通知」场景(如用户注册后,同时通知短信服务、邮件服务、积分服务)。
1.5.1 核心流程
- 创建 1 个 Fanout 交换机(如
hmall.fanout) - 创建多个队列(如
fanout.queue1、fanout.queue2),并绑定到该交换机 - 生产者发送消息到 Fanout 交换机
- 交换机将消息广播到所有绑定的队列,每个队列的消费者都能收到消息
1.5.2 步骤 1:手动创建交换机与队列(控制台操作)
1.5.2.1 创建队列
在 RabbitMQ 控制台创建 2 个队列:
- 队列 1:
fanout.queue1 - 队列 2:
fanout.queue2
(创建步骤同 1.2.1,参数默认)
1.5.2.2 创建 Fanout 交换机
- 进入 RabbitMQ 控制台的「Exchanges」页面,点击「Add a new exchange」
- 输入交换机名
hmall.fanout,选择类型「Fanout」,其他参数默认,点击「Add exchange」
1.5.2.3 绑定队列到交换机
- 在交换机列表中点击
hmall.fanout,进入交换机详情页 - 在「Bindings」区域,「To queue」选择
fanout.queue1,点击「Bind」 - 重复步骤 2,将
fanout.queue2也绑定到hmall.fanout - 绑定完成后,在交换机详情页可看到 2 个绑定关系
1.5.3 步骤 2:生产者发送广播消息
在 publisher 模块的 SpringAmqpTest 类中,添加发送 Fanout 消息的测试方法:
1 | /** |
1.5.4 步骤 3:消费者接收广播消息
在 consumer 模块的 SpringRabbitListener 类中,添加 2 个监听方法,分别监听 2 个队列:
1 | // 监听fanout.queue1,接收广播消息 |
1.5.5 步骤 4:测试广播效果
启动
consumer模块运行
publisher的testFanoutExchange方法发送消息查看
consumer控制台输出,会发现两个消费者都收到了相同的消息:1
2消费者1接收到Fanout消息:【hello, everyone! 这是一条系统公告~】
消费者2接收到Fanout消息:【hello, everyone! 这是一条系统公告~】
1.5.6 Fanout 交换机总结
- 转发规则:无视 RoutingKey,消息广播到所有绑定的队列
- 关键特性:交换机不存储消息,若没有绑定队列,消息会丢失
- 适用场景:全量通知(如服务启停通知、全局配置更新)
1.6 Direct 交换机(精准路由模式)
Fanout 交换机的广播模式无法筛选消息,而 Direct 交换机支持「精准路由」:队列绑定交换机时需指定「BindingKey」,生产者发送消息时需指定「RoutingKey」,仅当两者完全匹配时,消息才会被转发到该队列。
1.6.1 核心流程
- 创建 1 个 Direct 交换机(如
hmall.direct) - 创建多个队列,每个队列绑定交换机时指定 1 个或多个 BindingKey(如
red、blue) - 生产者发送消息时指定 RoutingKey
- 交换机仅将消息转发给 BindingKey 与 RoutingKey 完全匹配的队列
1.6.2 案例需求
- 交换机:
hmall.direct(类型 Direct) - 队列 1:
direct.queue1,绑定 BindingKey 为red、blue - 队列 2:
direct.queue2,绑定 BindingKey 为red、yellow - 测试场景 1:发送 RoutingKey 为
red的消息,验证两个队列是否都收到 - 测试场景 2:发送 RoutingKey 为
blue的消息,验证仅队列 1 收到
1.6.3 步骤 1:手动创建交换机与队列(控制台操作)
1.6.3.1 创建队列
在控制台创建 2 个队列:direct.queue1、direct.queue2(步骤同 1.2.1)。
1.6.3.2 创建 Direct 交换机
- 进入「Exchanges」页面,点击「Add a new exchange」
- 输入交换机名
hmall.direct,选择类型「Direct」,点击「Add exchange」
1.6.3.3 绑定队列与 BindingKey
- 点击
hmall.direct进入交换机详情页,在「Bindings」区域绑定队列:- 绑定
direct.queue1:「To queue」选direct.queue1,「Routing key」填red,点击「Bind」;重复此操作,再绑定blue - 绑定
direct.queue2:「To queue」选direct.queue2,「Routing key」填red,点击「Bind」;重复此操作,再绑定yellow
- 绑定
- 绑定完成后,交换机详情页会显示 4 条绑定关系(2 个队列 ×2 个 BindingKey)
1.6.4 步骤 2:消费者接收消息
在 consumer 模块的 SpringRabbitListener 类中,添加 2 个监听方法:
1 | // 监听direct.queue1 |
1.6.5 步骤 3:测试场景 1(RoutingKey=red)
在 publisher 模块的 SpringAmqpTest 类中,添加测试方法:
1 | /** |
测试结果:启动 consumer 后运行测试,两个消费者都会收到消息(因为两个队列的 BindingKey 都包含 red):
1 | 消费者1接收到direct.queue1的消息:【红色警报!日本乱排核废水,导致海洋生物变异,惊现哥斯拉!】 |
1.6.6 步骤 4:测试场景 2(RoutingKey=blue)
修改测试方法的 RoutingKey 为 blue:
1 |
|
测试结果:仅监听 direct.queue1 的消费者 1 收到消息(仅 direct.queue1 的 BindingKey 包含 blue):
1 | 消费者1接收到direct.queue1的消息:【最新报道,哥斯拉是居民自治巨型气球,虚惊一场!】 |
1.6.7 Direct 交换机总结
- 与 Fanout 的差异:Fanout 广播所有队列,Direct 仅转发给 BindingKey 匹配的队列
- 灵活扩展:若多个队列绑定相同的 BindingKey(如
red),则这些队列会同时收到消息(类似 Fanout 的局部广播) - 适用场景:精准路由(如订单支付成功后,仅通知订单服务更新状态)
1.7 Topic 交换机(通配符路由模式)
Direct 交换机的 RoutingKey 需完全匹配,而 Topic 交换机支持「通配符匹配」,更灵活。BindingKey 由多个单词组成(单词间用 . 分隔,如 china.news),支持两个通配符:
#:匹配 1 个或多个单词(如china.#可匹配china.news、china.weather.today)*:匹配恰好 1 个单词(如china.*可匹配china.news,但不能匹配china.weather.today)
1.7.1 核心流程
- 创建 1 个 Topic 交换机(如
hmall.topic) - 创建多个队列,绑定交换机时指定带通配符的 BindingKey(如
china.#、#.news) - 生产者发送消息时指定包含多个单词的 RoutingKey(如
china.news、japan.weather) - 交换机将消息转发给 BindingKey 与 RoutingKey 匹配的队列
1.7.2 案例需求
- 交换机:
hmall.topic(类型 Topic) - 队列 1:
topic.queue1,绑定 BindingKey 为china.#(匹配所有以china.开头的 RoutingKey,如china.news、china.weather) - 队列 2:
topic.queue2,绑定 BindingKey 为#.news(匹配所有以.news结尾的 RoutingKey,如china.news、japan.news) - 测试场景:发送 RoutingKey 为
china.news的消息,验证两个队列是否都收到
1.7.3 步骤 1:手动创建交换机与队列(控制台操作)
- 创建 2 个队列:
topic.queue1、topic.queue2 - 创建 Topic 交换机:
hmall.topic(类型选「Topic」) - 绑定队列与 BindingKey:
topic.queue1绑定china.#topic.queue2绑定#.news
1.7.4 步骤 2:生产者发送消息
在 publisher 模块的 SpringAmqpTest 类中,添加测试方法:
1 | /** |
1.7.5 步骤 3:消费者接收消息
在 consumer 模块的 SpringRabbitListener 类中,添加 2 个监听方法:
1 | // 监听topic.queue1(BindingKey=china.#) |
1.7.6 测试结果
启动 consumer 后运行测试,两个消费者都会收到消息:
topic.queue1的china.#匹配china.news(以china.开头)topic.queue2的#.news匹配china.news(以.news结尾)
控制台输出:
1 | 消费者1接收到topic.queue1的消息:【喜报!孙悟空大战哥斯拉,胜!】 |
1.7.7 Topic 交换机总结
- 与 Direct 的差异:Direct 需完全匹配,Topic 支持通配符,更灵活
- RoutingKey 格式:必须是多个单词(用
.分隔),否则通配符无法生效 - 适用场景:多维度筛选通知(如按「地区 + 类型」分类的新闻推送、日志按「服务名 + 级别」分类)
1.8 代码声明队列与交换机(生产环境推荐)
前面的案例通过 RabbitMQ 控制台手动创建队列和交换机,但生产环境中,队列、交换机的定义应与代码绑定(避免运维手动创建时出错)。SpringAMQP 支持通过代码自动声明:
- 启动项目时,Spring 会检查队列 / 交换机是否存在,不存在则自动创建
- 存在则跳过(不会覆盖已有配置)
1.8.1 核心 API
SpringAMQP 提供以下核心类用于声明组件:
| 组件 | 对应的类 / 接口 | 说明 |
|---|---|---|
| 队列 | org.springframework.amqp.core.Queue |
用于定义队列(名称、是否持久化等) |
| Fanout 交换机 | org.springframework.amqp.core.FanoutExchange |
用于定义 Fanout 类型交换机 |
| Direct 交换机 | org.springframework.amqp.core.DirectExchange |
用于定义 Direct 类型交换机 |
| Topic 交换机 | org.springframework.amqp.core.TopicExchange |
用于定义 Topic 类型交换机 |
| 绑定关系 | org.springframework.amqp.core.Binding |
用于定义队列与交换机的绑定(含 BindingKey) |
| 构建工具 | BindingBuilder、ExchangeBuilder |
简化交换机、绑定关系的创建 |
1.8.2 方式 1:基于 @Bean 声明(通用方式)
以 Fanout 交换机为例,在 consumer 模块创建配置类,通过 @Bean 声明队列、交换机及绑定关系:
1 | package com.itheima.consumer.config; |
效果:启动 consumer 模块后,Spring 会自动在 RabbitMQ 中创建 hmall.fanout 交换机、fanout.queue1、fanout.queue2,并完成绑定。
1.8.3 方式 2:基于 @Bean 声明 Direct 交换机(多 BindingKey 场景)
Direct 交换机需为队列绑定多个 BindingKey(如 red、blue),可通过多个 Binding Bean 实现:
1 | package com.itheima.consumer.config; |
1.8.4 方式 3:基于注解声明(简化方式)
通过 @RabbitListener 注解的 bindings 属性,可在监听方法上直接声明队列、交换机及绑定关系,无需单独编写配置类,更简洁。
1.8.4.1 注解声明 Direct 交换机
1 | import org.springframework.amqp.core.ExchangeTypes; |
1.8.4.2 注解声明 Topic 交换机
1 | // 监听topic.queue1,声明队列与Topic交换机(BindingKey=china.#) |
推荐场景:若队列仅对应一个消费者,优先使用注解声明(代码更集中);若多个消费者共享一个队列,建议用 @Bean 声明(避免重复创建)。
1.9 消息转换器(解决 JDK 序列化问题)
SpringAMQP 发送消息时,会将消息对象序列化为字节数组;接收消息时,再将字节数组反序列化为 Java 对象。默认使用JDK 序列化,但存在明显缺陷:
- 消息体积大(包含类结构信息)
- 可读性差(控制台查看是乱码)
- 有安全漏洞(可能反序列化恶意类)
生产环境中推荐使用JSON 序列化,需手动配置 Jackson2JsonMessageConverter。
1.9.1 步骤 1:测试默认 JDK 序列化(问题演示)
1.9.1.1 声明测试队列(代码方式)
在 consumer 模块创建配置类,声明队列 object.queue:
1 | package com.itheima.consumer.config; |
启动 consumer 模块,Spring 会自动创建 object.queue 队列。
1.9.1.2 发送对象消息(Map 类型)
在 publisher 模块的 SpringAmqpTest 类中,添加发送 Map 对象的测试方法:
1 | import java.util.HashMap; |
1.9.1.3 查看 JDK 序列化的问题
运行测试方法后,进入 RabbitMQ 控制台的 object.queue 详情页,点击「Get Message (s)」查看消息:
- 消息体是乱码的字节数组(如
¬í 4 java.util.HashMap...) - 体积较大(仅简单 Map 就占用数百字节)
- 无法直接查看消息内容,调试困难
1.9.2 步骤 2:配置 JSON 消息转换器
1.9.2.1 导入 Jackson 依赖(若未引入)
若项目未引入 SpringMVC(spring-boot-starter-web),需手动添加 Jackson 依赖(publisher 和 consumer 都需添加):
1 | <dependency> |
(若已引入 spring-boot-starter-web,则无需重复添加,Web 依赖已包含 Jackson)
1.9.2.2 配置 JSON 转换器(代码方式)
在 publisher 和 consumer 模块的启动类中,添加 MessageConverter 的 @Bean:
1 | import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter; |
注意:consumer 模块的启动类需做相同配置,确保接收时能反序列化为 JSON。
1.9.3 步骤 3:测试 JSON 序列化效果
- 先删除
object.queue中已有的 JDK 序列化消息(控制台操作) - 重新运行
publisher的testSendMap方法发送 Map 对象 - 进入 RabbitMQ 控制台查看
object.queue的消息:- 消息体为清晰的 JSON 格式:
{"name":"柳岩","age":21} - 体积大幅减小(仅数十字节)
- 包含自动生成的
messageId(如spring-amqp-123456),便于后续幂等性处理
- 消息体为清晰的 JSON 格式:
1.9.4 步骤 4:消费者接收 JSON 消息
在 consumer 模块的 SpringRabbitListener 类中,添加监听 object.queue 的方法,直接用 Map 接收消息(Spring 会自动将 JSON 反序列化为 Map):
1 | // 监听object.queue,接收JSON格式的Map消息 |
测试结果:启动 consumer 后发送消息,控制台输出:
1 | 消费者接收到object.queue消息:【{name=柳岩, age=21}】 |
1.9.5 消息转换器总结
- 推荐使用
Jackson2JsonMessageConverter,解决 JDK 序列化的缺陷 - 需在生产者和消费者两端同时配置,确保序列化 / 反序列化格式一致
- 支持复杂对象(如自定义 POJO),Spring 会自动将 JSON 与 POJO 属性映射(需保证字段名一致)
1.10 业务改造(余额支付异步通知)
基于前面的知识,我们对「余额支付功能」进行改造:将支付成功后同步调用订单服务(OpenFeign),改为异步发送消息(RabbitMQ),降低服务间耦合,提高系统容错性。
1.10.1 改造需求
- 交换机:
pay.direct(Direct 类型,用于支付相关消息路由) - 队列:
trade.pay.success.queue(订单服务监听的队列,用于接收支付成功通知) - 绑定关系:队列
trade.pay.success.queue绑定到pay.direct,BindingKey 为pay.success - 流程:
- 支付服务(pay-service)支付成功后,发送消息到
pay.direct,RoutingKey 为pay.success,消息内容为订单 ID - 订单服务(trade-service)监听
trade.pay.success.queue,接收消息后更新订单状态为「已支付」
- 支付服务(pay-service)支付成功后,发送消息到
1.10.2 步骤 1:通用配置(支付服务与订单服务)
两个服务都需添加 SpringAMQP 依赖和 MQ 连接配置。
1.10.2.1 添加依赖
在 pay-service 和 trade-service 的 pom.xml 中,添加 SpringAMQP 依赖:
1 | <!--SpringAMQP依赖,用于消息收发--> |
1.10.2.2 配置 MQ 连接
在两个服务的 application.yml 中,添加 RabbitMQ 连接配置(替换为实际环境信息):
1 | spring: |
1.10.3 步骤 2:订单服务(trade-service)接收消息
订单服务作为消费者,需监听 trade.pay.success.queue 队列,接收支付成功消息并更新订单状态。
1.10.3.1 编写消息监听类
在 trade-service 的 com.hmall.trade.listener 包下,创建 PayStatusListener 类:
1 | package com.hmall.trade.listener; |
1.10.3.2 订单服务方法(markOrderPaySuccess)
假设 IOrderService 接口已存在 markOrderPaySuccess 方法,用于更新订单状态(核心逻辑如下):
1 | /** |
1.10.4 步骤 3:支付服务(pay-service)发送消息
支付服务作为生产者,需在支付成功后发送消息到 pay.direct 交换机。
1.10.4.1 注入 RabbitTemplate
在支付服务的支付订单实现类(PayOrderServiceImpl)中,注入 RabbitTemplate:
1 | import org.springframework.amqp.rabbit.core.RabbitTemplate; |
1.10.4.2 改造支付成功逻辑
修改 tryPayOrderByBalance 方法,删除原有的 OpenFeign 同步调用,改为发送 RabbitMQ 消息:
1 | import com.hmall.common.exception.BizIllegalException; |
1.10.5 改造效果与优势
- 降低耦合:支付服务无需依赖订单服务的 Feign 接口,服务间通过消息通信,减少直接依赖
- 提高容错:若订单服务暂时不可用,消息会暂存于 RabbitMQ,待服务恢复后再消费,避免同步调用时的服务降级
- 异步解耦:支付服务发送消息后即可返回,无需等待订单服务处理完成,提升支付接口响应速度
- 最终一致性:通过消息队列保证消息不丢失(后续可配合消息确认机制、定时重试),确保订单状态最终会更新
-
感谢你赐予我前进的力量