RabbitMQ实战项目
- 课程测评结论与相关资料
- MQ基础篇
-
- [第1节 - RabbitMQ课程介绍](#第1节 - RabbitMQ课程介绍)
- [第2节 - 初始MQ-同步调用](#第2节 - 初始MQ-同步调用)
- [第3节 - 初始MQ-异步调用](#第3节 - 初始MQ-异步调用)
- [第4节 - 初始MQ-技术选型](#第4节 - 初始MQ-技术选型)
- [第5节 - RabbitMQ - 认识和安装](#第5节 - RabbitMQ - 认识和安装)
- [第6节 - RabbitMQ - 快速入门](#第6节 - RabbitMQ - 快速入门)
- [第7节 - RabbitMQ - 数据隔离](#第7节 - RabbitMQ - 数据隔离)
- [第8节 - Java客户端 - 快速入门](#第8节 - Java客户端 - 快速入门)
- [第9节 - Java客户端 - work模型](#第9节 - Java客户端 - work模型)
- [第10节 - Java客户端 - Fanout交换机](#第10节 - Java客户端 - Fanout交换机)
- [第11节 - Java客户端 - Direct交换机](#第11节 - Java客户端 - Direct交换机)
- [第12节 - Java客户端 - Topic交换机](#第12节 - Java客户端 - Topic交换机)
- [第13节 - Java客户端 - 用Java代码声明队列(基于Bean)](#第13节 - Java客户端 - 用Java代码声明队列(基于Bean))
- [第14节 - Java客户端 - 用Java代码声明队列(基于注解)](#第14节 - Java客户端 - 用Java代码声明队列(基于注解))
- [第15节 - Java客户端 - 消息转换器](#第15节 - Java客户端 - 消息转换器)
- [第16节 - 基于余额支付功能将传统业务代码改造为MQ模式](#第16节 - 基于余额支付功能将传统业务代码改造为MQ模式)
- MQ高级篇
课程测评结论与相关资料
这是一门结构清晰、面向求职与实战的RabbitMQ课程,覆盖了"会用"到"用好"的核心知识链路,对Java开发者的日常工作与面试准备有极强的针对性。章节内容均为本人实战测评,有需要的伙伴可以先通过本篇课程笔记,判断课程内容是否符合您的学习目标。
课程名称:《黑马程序员RabbitMQ入门到实战教程》- [黑马程序员]
适合人群:
- 有一定Java/Spring Boot基础,需要快速上手RabbitMQ的开发者。
- 希望了解消息队列在项目中实际应用的初学者。
Gitee仓库地址:RabbitMQ实战项目
视频教程地址:黑马程序员RabbitMQ入门到实战教程,MQ消息中间件,微服务rabbitmq消息队列实战,rabbitmq面试题一套全覆盖
补充:视频教程中包含学习笔记,与本篇内容的一致的,可供大家查阅
大家有想要学习的内容可以在评论区留言,博主会不定期学习新内容并提供学习笔记,争取为大家找出最简单易懂的学习教程(理论+实战),不要从入门到放弃呀!!!

MQ基础篇
第1节 - RabbitMQ课程介绍
基础篇目录(常用)
- 同步和异步
- MQ技术选型
- 数据隔离
- SpringAMQP
- work模式
- MQ消息转换器
- 发布订阅模式
- 消息堆积问题处理
高级篇目录(常见问题和面试)
- 发送者重连
- 发送者确认
- MQ持久化
- LazyQueue
- 消费者确认
- 失败重试
- 业务幂等
- 延迟消息
第2节 - 初始MQ-同步调用

什么时候需要用同步调用?
答:下级服务需要上级服务的结果
同步调用的优势是什么?
答:时效性强,等待到结果后才返回
同步调用存在的问题是什么?
答:扩展性差、性能下降、级联失败
第3节 - 初始MQ-异步调用
异步调用的三个角色:消息发送者、消息代理、消息接收者

异步可以把"交易服务"、"通知服务"、"积分服务"隔离开,不影响"支付服务"

异步调用的优势:
解除耦合,扩展性强
无需等待,性能好
故障隔离
缓存消息,流量削峰填谷

异步调用的问题是什么?
答:
不能立即得到调用结果,时效性差;
不确定下游业务执行是否成功;
业务安全依赖于Broker(消息代理)的可靠性;
什么场景下适合异步调用?
答:
执行结果对业务没有太大的影响
对性能要求较高的情况;
第4节 - 初始MQ-技术选型
MQ:Message Queue,消息队列

一般都是选择RabbitMQ和RocketMQ
日志选择使用Kafka
第5节 - RabbitMQ - 认识和安装
RabbitMQ官方网站:https://www.rabbitmq.com
RabbitMQ的整体架构和核心概念:
- virtual-host:虚拟主机,起到数据隔离的作用
- publisher:消息发送者
- consumer:消息的消费者
- queue:队列,存储消息
- exchange:交换机,负责路由消息

一个MQ可以有多个虚拟主机,不同的VirtualHost中有自己的交换机和队列

第6节 - RabbitMQ - 快速入门
操作流程:
- 创建队列;
- 选择交换机;
- 把交换机与队列进行绑定;
- 在交换机中模拟发送消息;
第7节 - RabbitMQ - 数据隔离
操作流程:
- 创建新用户,暂无可使用的虚拟主机
- 登录新用户,创建虚拟主机
每一个项目创建一个用户账号,相互之间互不影响
Virtual host是虚拟主机名

实战案例:创建新用户,在为它创建一个虚拟主机
需求:在RabbitMQ的控制台完成下列操作:
1.新建一个用户hmall
2.为hmall用户创建一个virtual host
3.·测试不同virtual host之间的数据隔离现象
操作步骤如下:
1.创建用户hmall,填写用户名和密码,设置Tag标签代表不同的权限;

使用刚刚创建的新用户hmall登录,我是超级管理员权限能看到全部的队列,但是无法操作。
2.创建虚拟主机/hmall

可以看到新建的虚拟主机

自动创建的交换机

总结:开发中应该为每一个项目创建一个自己的用户和一个自己的虚拟主机,相互之间没有影响。
第8节 - Java客户端 - 快速入门
RabbitMQ官方教程地址:https://www.rabbitmq.com/tutorials/tutorial-one-java
Spring官方文档:https://spring.io/projects
AMQP是一种协议;
SpringAMQP是一个API接口;
Spring-RabbitMQ是底层实现;
实战案例:快速入门案例
需求如下:
1、利用控制台创建队列simple.queue
2、在publisher服务中,利用SpringAMQP直接向simple.queue发送消息
3、在consumer服务中,利用SpringAMQP编写消费者,监听simple.queue队列

操作步骤:
RabbitMQ控制台中创建队列

整体项目结构:

1.在父工程中引入"spring-amqp"依赖
xml
<!--AMQP依赖,包含RabbitMQ-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2.在每个微服务中引入MQ服务端信息,配置文件中写入
yml
spring:
rabbitmq:
host: 192.168.150.101 #主机名(改成自己的)
port: 5672 #端口号
username: hmall #用户名
password: 123 #密码
3.发送消息,springAMQP提供了RabbitTemplate工具类。发送消息代码如下:
java
package com.example.publisher;
import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.HashMap;
import java.util.Map;
@SpringBootTest
public class SpringAmqpTest {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
void testSimpleQueue() {
// 队列名称
String queueName = "simple.queue";
// 消息
String message = "hello, amqp!";
// 发送消息
rabbitTemplate.convertAndSend(queueName, message);
}
}

4.消息接收:SpringAMQP提供声明式的消息监听,通过注解在方法上声明要监听的队列名称,SpringAMQP就会把消息传递给当前方法。
java
package com.example.consumer.listeners;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.messaging.converter.MessageConversionException;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class SpringRabbitListener {
@RabbitListener(queues = "simple.queue")
public void listenSimpleQueueMessage(String msg) {
log.info("spring消费者接收到消息:【" + msg + "】");
if (true) {
throw new MessageConversionException("故意抛出异常");
}
log.info("消息处理完成");
}
}
SpringAMQP如何收发消息?
答:
1.引入spring-boot-starter-amqp依赖
2.配置rabbitmq服务端信息
3.利用RabbitTemplate发送消息
4.利用@RabbitListener注解声明要监听的队列,监听消息
第9节 - Java客户端 - work模型
Work queues,任务模型
让多个消费者绑定到一个队列,共同消费队列中的消息

实战案例:work模型-让多个消费者绑定到一个队列
需求:work模型-让多个消费者绑定到一个队列
- 在RabbitMQ的控制台创建一个队列,名为work.queue
- 在publisher服务中定义测试方法,在1秒内产生50条消息,发送到work.queue
- 在consumer服务中定义两个消息监听者,都监听work.queue队列
- 消费者1每秒处理50条消息,消费者2每秒处理5条消息
操作步骤如下:
1.创建队列

2.编写两个消费者,接收速度相同
代码文件地址:com.example.consumer.listeners.SpringRabbitListener
java
@RabbitListener(queues = "work.queue")
public void listenWorkQueue1(String msg) {
System.out.println("消费者1收到了work.queue的消息: 【" + msg + "】 ");
}
@RabbitListener(queues = "work.queue")
public void listenWorkQueue2(String msg) {
System.err.println("消费者2收到了work.queue的消息: 【" + msg + "】 ");
}
3.编写生产者代码,发送50条消息,每条信息间隔20毫秒
代码文件地址:com.example.publisher.SpringAmqpTest
java
@Test
void testWorkQueue() throws InterruptedException {
String queueName = "work.queue";
for (int i = 1; i <= 50; i++) {
String msg = "hello, worker, message_" + i;
rabbitTemplate.convertAndSend(queueName, msg);
Thread.sleep(20); // 休眠20毫秒
}
}
4.观察执行结果:同一条消息只会被一个消费者获取,消费的方式是轮询

5.变更消费者的处理消息速度
java
@Slf4j
@Component
public class SpringRabbitListener {
@RabbitListener(queues = "work.queue")
public void listenWorkQueue1(String msg) throws InterruptedException {
System.out.println("消费者1收到了work.queue的消息: 【" + msg + "】 ");
Thread.sleep(20);
}
@RabbitListener(queues = "work.queue")
public void listenWorkQueue2(String msg) throws InterruptedException {
System.err.println("消费者2收到了work.queue的消息: 【" + msg + "】 ");
Thread.sleep(200);
}
}
6.观察消息处理结果:依然采用的轮询消费,没有考虑消费者的处理能力

7.解决现在出现问题
消费者消息推送限制
默认情况下,RabbitMQ会将消息依次轮询投递给绑定在队列上的每一个消费者,但是并没有考虑到消费者是否已经处理完消息,可能出现消息堆积的情况。
解决办法:修改application.yml,设置preFetch值为1,确保同一时刻最多投递给消费者1条消息:
yml
spring:
rabbitmq:
listener:
simple:
prefetch: 1 #每次只能获取一条消息,处理完成才能获取下一个消息
8.观察消息处理结果:处理消息快的,就多处理一些消息

Work模型的使用:
多个消费者绑定到一个队列,可以加快消息处理速度;
同一条消息只会被一个消费者处理;
通过设置prefetch来控制消费者预取的消息数量,处理完一条再处理下一条,实现能者多劳。
第10节 - Java客户端 - Fanout交换机
真正的生产环境都会经过exchange来发送消息,交换机的类型有三种:
- Fanout:广播
- Direct:定向
- Topic:话题
Fanout Exchange会接收到的消息广播到每一个跟它绑定的queue,所以也叫广播模式。

实战案例:利用SpringAMQP演示FanoutExchange的使用
实现思路如下:
1.在RabbitMQ控制台中,声明队列fanout.queue1和fanout.queue2
2.在RabbitMQ控制台中,声明交换机hmall.fanout,将两个队列与其绑定
3.在consumer服务中,编写两个消费者方法,分别监听fanout.queue1和fanout.queue2
4.在publisher中编写测试方法,向hmall.fanout发送消息
操作步骤如下:
1.创建队列

2.创建交换机

3.绑定交换机和队列

4.代码编写监听器,并启动服务
java
@Slf4j
@Component
public class SpringRabbitListener {
@RabbitListener(queues = "fanout.queue1")
public void listenFanoutQueue1(String msg) {
System.out.println("消费者1收到了fanout.queue1的消息: 【" + msg + "】 ");
}
@RabbitListener(queues = "fanout.queue2")
public void listenFanoutQueue2(String msg) {
System.out.println("消费者2收到了fanout.queue2的消息: 【" + msg + "】 ");
}
}
5.代码编写消息发送(单元测试)
java
@SpringBootTest
public class SpringAmqpTest {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void testSendFanout() {
// 交换机名称
String exchangeName = "hmall.fanout";
// 消息
String message = "hello, everyone";
// 发送消息
rabbitTemplate.convertAndSend(exchangeName, null, message);
}
}
6.执行单元测试,查看控制台输出

交换机的作用是什么?
答:
1.接收publisher发送的消息
2.将消息按照规则路由到与之绑定的队列
3.FanoutExchange的会将消息路由到每个绑定的队列
第11节 - Java客户端 - Direct交换机
业务场景:我需要把消息发送给不同的人
Direct Exchange会将接收到的消息根据规则路由到指定的Queue,因此称为定向路由。
- 每一个Queue都与Exchange设置一个BindingKey
- 发布者发送消息时,指定消息的RoutingKey
- Exchange将消息路由到BindingKey与消息RoutingKey一致的队列

不同的队列也可以绑定同样的bindingKey,效果就与Fanout交换机一样
实战案例:利用SpringAMQP演示DirectExchange的使用
需求如下:
- 在RabbitMQ控制台中,声明队列direct.queue1和direct.queue2
- 在RabbitMQ控制台中,声明交换机hmall.direct,将两个队列与其绑定
- 在consumer服务中,编写两个消费者方法,分别监听direct.queue1和direct.queue2
- 在publisher中编写测试方法,利用不同的RoutingKey向hmall.direct发送消息
操作步骤如下:
1.创建队列

2.创建交换机

3.绑定交换机和队列

4.代码编写监听器,并启动服务
java
@Slf4j
@Component
public class SpringRabbitListener {
@RabbitListener(queues = "direct.queue1")
public void listenDirectQueue1(String msg) {
System.out.println("消费者1收到了direct.queue1的消息: 【" + msg + "】 ");
}
@RabbitListener(queues = "direct.queue2")
public void listenDirectQueue2(String msg) {
System.out.println("消费者2收到了direct.queue2的消息: 【" + msg + "】 ");
}
}
5.代码编写消息发送(单元测试)
java
@SpringBootTest
public class SpringAmqpTest {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void testSendDirect() {
// 交换机名称
String exchangeName = "hmall.direct";
// 消息
String message = "蓝色通知!";
// 发送消息
rabbitTemplate.convertAndSend(exchangeName, "blue", message);
}
}
6.执行单元测试,查看控制台输出

第12节 - Java客户端 - Topic交换机
TopicExchange与DirectExchange类似,区别在于routingKey可以是多个单词的列表,并且以 " . " 分割。
routingKey可以是多个单词的列表,并且以 " . " 分割

实战案例:利用SpringAMQP演示TopicExchange的使用
需求如下:
- 在RabbitMQ控制台中,声明队列topic.queue1和topic.queue2
- 在RabbitMQ控制台中,声明交换机hmall.topic,将两个队列与其绑定
- 在consumer服务中,编写两个消费者方法,分别监听topic.queue1和topic.queue2
- 在publisher中编写测试方法,利用不同的RoutingKey向hmall.topic发送消息
操作步骤如下:
1.新建队列

2.创建交换机

3.绑定交换机和队列

4.代码编写监听器,并启动服务
java
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
public class SpringRabbitListener {
@RabbitListener(queues="topic.queue1")
public void listenTopicQueue1(String msg) {
System.out.println("消费者1收到了topic.queue1的消息: 【" + msg +"】 ");
}
@RabbitListener(queues ="topic.queue2")
public void listenTopicQueue2(String msg) {
System.out.println("消费者2收到了topic.queue2的消息: 【" + msg +"】 ");
}
}
5.代码编写消息发送(单元测试)
java
import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class TestClass {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void testSendTopic() {
// 交换机名称
String exchangeName= "hmall.topic";
// 消息
String message = "蓝色通知!";
// 发送消息
rabbitTemplate.convertAndSend(exchangeName,"japan.news", message);
}
}
6.执行单元测试,查看控制台输出
routingKey = "japan.news"

routingKey = "china.news"

描述下Direct交换机与Topic交换机的差异?
答:
Topic交换机接收的消息RoutingKey可以是多个单词,以 " . " 分割
Topic交换机与队列绑定时的bindingKey可以指定通配符
:代表0个或多个词
* :代表1个词
第13节 - Java客户端 - 用Java代码声明队列(基于Bean)
SpringAMQP提供了几个类,用来声明队列、交换机及其绑定关系:
- Queue:用于声明队列,可以用工厂类QueueBuilder构建
- Exchange:用于声明交换机,可以用工厂类ExchangeBuilder构建
- Binding:用于声明队列和交换机的绑定关系,可以用工厂类BindingBuilder构建

实战示例:声明一个Fanout类型的交换机,并且创建队列与其绑定
需求:声明一个Fanout类型的交换机,并且创建队列与其绑定
备注:队列和交换机的声明,一般是在消费者那边声明
操作步骤如下(Fanout):
1.编写Java代码,生成队列、交换机、绑定关系
java
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
public class FanoutConfiguration {
// 声明交换机
@Bean
public FanoutExchange fanoutExchange() {
// return ExchangeBuilder.fanoutExchange("hma11.fanout").build();
return new FanoutExchange("hmall.fanout2");
}
// 声明队列
@Bean
public Queue fanoutQueue3() {
// QueueBuilder.durable("").build(); //持久化
return new Queue("fanout.queue3"); // 默认持久
}
// 声明队列
@Bean
public Queue fanoutQueue4() {
return new Queue("fanout.queue4"); // 默认持久
}
// 绑定队列和交换机(队列3和交换机)
@Bean
public Binding fanoutBinding3(Queue fanoutQueue3, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(fanoutQueue3).to(fanoutExchange);
}
// 绑定队列和交换机(队列4和交换机)
@Bean
public Binding fanoutBinding4() {
return BindingBuilder.bind(fanoutQueue4()).to(fanoutExchange());
}
}
2.重启服务查看执行效果
查看队列

查看交换机

绑定关系

第14节 - Java客户端 - 用Java代码声明队列(基于注解)
基于"注解"去声明队列、交换机和绑定关系
实战示例:基于@RabbitListener注解来声明队列和交换机的方式
需求:基于@RabbitListener注解来声明队列和交换机
改造前:基于Bean来声明,非常的麻烦,每一次绑定都需要新建Bean
java
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class DirectConfiguration {
@Bean
public DirectExchange directExchange() {
return new DirectExchange("hmall.direct");
}
@Bean
public Queue directQueue1() {
return new Queue("direct.queue1");
}
@Bean
public Queue directQueue2() {
return new Queue("direct.queue2");
}
@Bean
public Binding directBindingRed(Queue directQueue1, DirectExchange directExchange){
return BindingBuilder.bind(directQueue1).to(directExchange()).with("red");
}
@Bean
public Binding directBindingBlue() {
return BindingBuilder.bind(directQueue1()).to(directExchange()).with("blue");
}
}
改造后:基于注解声明
java
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
public class SpringRabbitListener {
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "direct.queue1", durable = "true"),
exchange = @Exchange(name = "hmall.direct", type = ExchangeTypes.DIRECT),
key = {"red", "blue"}
))
public void listenDirectQueue1(String msg) {
System.out.println("消费者1收到了direct.queue1的消息: 【" + msg + "】 ");
}
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "direct.queue2", durable = "true"),
exchange = @Exchange(name = "hmall.direct", type = ExchangeTypes.DIRECT),
key = {"red", "yellow"}
))
public void listenDirectQueue2(String msg) {
System.out.println("消费者2收到了direct.queue2的消息: 【" + msg + "】 ");
}
}
快捷键:ctrl+p 提示注解参数有哪些。
声明队列、交换机、绑定关系的Bean是什么?
答:
Queue
FanoutExchange、DirectExchange、TopicExchange
Binding
基于@RabbitListener注解声明队列和交换机有哪些常见注解?
答:
@Queue
@Exchange
第15节 - Java客户端 - 消息转换器
实战示例:测试利用SpringAMQP发送对象类型的消息
需求说明:
- 声明一个队列,名为object.queue
- 编写单元测试,向队列中直接发送一条消息,消息类型为Map
- 在控制台查看消息,总结你能发现的问题
操作步骤如下:
1.创建队列

2.编写代码,发送消息(单元测试)
java
import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.HashMap;
import java.util.Map;
@SpringBootTest
public class RabbitMqTest {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
void testSendObject() {
// 队列名称
String queueName = "object.queue";
// 消息
Map<String, Object> message = new HashMap<>(2);
message.put("name", "jack");
message.put("age", 21);
// 发送消息
rabbitTemplate.convertAndSend(queueName, message);
}
}
3.执行单元测试后,查看队列,可以看到消息已经进入到队列中

Get一下,获取到Jack,被JDK的序列化转化为字节了

CTRL+H(hierarchy 结构) :IDEA中接口的所有实现类查看
总结发现问题如下:
Spring的对消息对象的处理是由org.springframework.amqp.support.converter.MessageConverter来处理的。而默认实现是SimpIeMessageConverter,基于JDK的ObjectOutputStream完成序列化。
存在下列问题:
- JDK序列化有安全风险
- JDK序列化的消息太大
- JDK序列化的消息可读性差
解决方案:采用JSON序列化代替默认的JDK序列化
操作步骤如下:
1.在publisher和consumer中都要引入jackson依赖(直接在父工程中引入)
xml
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
2.在publisher和consumer中都要配置MessageConverter

java
@Bean
public MessageConverter messageConverter() {
return new Jackson2JsonMessageConverter();
}
3.重新发送消息,然后在控制台中查看消息

- 编写消费者代码,重新启动服务
java
@Slf4j
@Component
public class SpringRabbitListener {
@RabbitListener(queues = "object.queue")
public void listenObjectQueue(Map<String, Object> msg) {
System.out.println("消费者1收到了object.queue的消息: 【" + msg + "】 ");
}
}
可以发现第1条消息处理失败了,第2条消息成功。

此时第1条消息未被正常处理,但是在队列中却消失了,这叫做 "消息丢失"。

第16节 - 基于余额支付功能将传统业务代码改造为MQ模式
需求:改造余额支付功能,不再同步调用交易服务的OpenFeign接口,而是采用异步MQ通知交易服务更新订单状态。

操作步骤:
1.在交易服务中配置amqp的依赖和json消息转换器(SpringMVC自带了JSON转换器)
xml
<!--AMQP依赖,包含RabbitMQ-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!--Jackson依赖,用于序列化-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
2.在通用模块的中去定义MqConfig
java
package com.example.common.config;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MqConfig {
@Bean
public MessageConverter messageConverter() {
return new Jackson2JsonMessageConverter();
}
}
3.在交易服务中的application.yml中配置rabbitmq的地址
yml
spring:
rabbitmq:
host: 192.168.150.101 #主机名(改成自己的)
virtual-host: /hmall #虚拟主机名
port: 5672 #端口号
username: hmall #用户名
password: 123 #密码
4.在交易服务中编写监听器(消费者)代码
改造前:

改造后:

启动服务,队列和交换机会自动生成
5.在支付服务中配置amqp的依赖和json消息转换器(SpringMVC自带了JSON转换器)
xml
<!--AMQP依赖,包含RabbitMQ-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!--Jackson依赖,用于序列化-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
6.在支付服务中的application.yml中配置rabbitmq的地址
yml
spring:
rabbitmq:
host: 192.168.150.101 #主机名(改成自己的)
virtual-host: /hmall #虚拟主机名
port: 5672 #端口号
username: hmall #用户名
password: 123 #密码
7.在支付服务中编写生产者代码,发送消息
改造前:

改造后:
在服务中引入 RabbitTemplate
java
@Autowired
private RabbitTemplate rabbitTemplate;

MQ高级篇

持续更新中...
