文章目录
一、MQ的基本结构:
RabbitMQ中的一些角色:
- publisher:生产者(消息发给交换机——交换机负责路由)
- consumer:消费者(从队列中获取消息、处理消息)
- exchange个:交换机,负责消息路由
- queue:队列,存储消息
- 【 virtualHost:虚拟主机】隔离不同租户的exchange、queue等资源的逻辑分组【消息的隔离】
二、RabbitMQ消息模型
RabbitMQ官方提供了5个不同的Demo示例,对应了不同的消息模型:
三、快速入门(基本消息队列)(了解即可)
基本队列模式的模型图:(RabbitMQ实现)
基本队列模式只包括三个角色:
- publisher:消息发布者,将消息发送到队列queue
- queue:消息队列,负责接受并缓存消息
- consumer:订阅队列,处理队列中的消息
1)、创建SpringBoot项目,并创建两个子模块
包括三部分:
- mq-demo:父工程,管理项目依赖
- publisher:消息的发送者
- consumer:消息的消费者
附:如果不会创建,可以查看下面文章:IDEA创建maven多级模块项目
父工程的pom.xml文件中
<!--父项目:所有的依赖交给SpringBoot管理-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.9.RELEASE</version>
<relativePath/>
</parent>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<build>
<plugins>
<!--打包插件-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
2)、publisher实现
- 建立连接
- 创建Channel
- 声明队列
- 发送消息
- 关闭连接和channel
代码如下:
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import org.junit.Test;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class PublisherTest {
@Test
public void testSendMessage() throws IOException, TimeoutException {
// 1.建立连接
ConnectionFactory factory = new ConnectionFactory();
// 1.1.设置连接参数,分别是:主机名、端口号、vhost、用户名、密码
factory.setHost("10.65.184.58");
factory.setPort(5672);
factory.setVirtualHost("/");
factory.setUsername("itcast");
factory.setPassword("123321");
// 1.2.建立连接
Connection connection = factory.newConnection();
// 2.创建通道Channel
Channel channel = connection.createChannel();
// 3.创建队列
String queueName = "simple.queue";
channel.queueDeclare(queueName, false, false, false, null);
// 4.发送消息
String message = "hello, rabbitmq!";
channel.basicPublish("", queueName, null, message.getBytes());
System.out.println("发送消息成功:【" + message + "】");
// 5.关闭通道和连接
channel.close();
connection.close();
}
}
3)consumer实现
前三步和消费者一样
- 建立connection
- 创建channel
- 利用channel声明队列
- 【订阅消息】:
1、消费者定义consumer的消费行为handleDelivery()——这是一个回调函数
2、利用channel将消费者与队列绑定
hannel.basicConsume(queueName, true, new DefaultConsumer(channel){…}
代码如下:
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class ConsumerTest {
public static void main(String[] args) throws IOException, TimeoutException {
// 1.建立连接
ConnectionFactory factory = new ConnectionFactory();
// 1.1.设置连接参数,分别是:主机名、端口号、vhost、用户名、密码
factory.setHost("10.65.184.58");
factory.setPort(5672);
factory.setVirtualHost("/");
factory.setUsername("itcast");
factory.setPassword("123321");
// 1.2.建立连接
Connection connection = factory.newConnection();
// 2.创建通道Channel
Channel channel = connection.createChannel();
// 3.创建队列
String queueName = "simple.queue";
channel.queueDeclare(queueName, false, false, false, null);
// 4.订阅消息(消费/处理了一条消息,队列消息-1)
//4.1channel.basicConsume(queueName, true, new DefaultConsumer(channel)方法:是绑定队列queueName和消费者
//每当队列中有1条消息,会自动执行回调函数new DefaultConsumer(channel){....}里面的handleDelivery
channel.basicConsume(queueName, true, new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException {
// 4.2、处理消息(模拟处理,这里只是打印了一下)
String message = new String(body);
System.out.println("接收到消息:【" + message + "】");
}
});
System.out.println("等待接收消息。。。。");
}
}
附:回调函数是什么?
回调函数:函数的其中一个参数也是一个函数
教程』回调函数是个啥?
下图:
hannel.basicConsume(…)方法
参数: new DefaultConsumer(channel){…}——匿名函数
4)、测试结果如下:
说明1:生产者和消费者的启动顺序是不确定先后的,所有都需要写创建队列,但是如果通道中已经有了队列simple.queue,那么就不会创建新的队列,而是使用已经存在的队列
说明2:利用channel将消费者与队列绑定,
步骤4.1中,channel.basicConsume(queueName, true, new DefaultConsumer(channel)方法:是绑定队列queueName和消费者;此时并不会执行回调函数里面的方法,
而是监听队列,当队列中有消息,会自动执行回调函数new DefaultConsumer(channel){…}里面的handleDelivery方法(异步处理)
四、SpringAMQP
从上面的代码可以看出,publisher和consumer的实现有很多重复部分,现在只是一个发布者和消费者,以后每次都得写这种冗余的代码?我们应该如何优化他们呢?
这里就要引入SpringAMQP
什么是SpringAMQP?
Spring AMQP 是 Spring 对 AMQP 协议的封装和扩展,提供了消息发送和接收的模板。Spring AMQP项目将核心Spring概念应用于基于 AMQP 的消息传递解决方案的开发,以便更容易和简单的管理AMQO资源。
什么是AMQP?高级消息队列协议
Advanced Message Queuing ProtocoL, 是【用于在应用程序之间传递业务消息的开放标准】,
【该协议与语言和平台无关,更符合微服务中独立性的要求】
总结:
AMQP:是高级消息队列协议——消息队列的规范
SpringAMQP是Spring对AMQP的一种实现(且提供了一种模板/API规范,来发送和接收消息)
SpringAMQP提供了三个功能:
- RabbitAdmin用于【自动声明队列】、交换机及其绑定关系
(手动创建队列就像上面的入门案例一样) - 基于注解的监听器(容器)模式,用于异步接收消息
- 封装了RabbitTemplate工具,一般用于发送消息 (也可以接收,但是接收一般是用监听器容器去做)
如果SpringAMQP如何完成这些功能感兴趣,可以查看下面文章
Spring AMQP简介和使用
这三个帮助我们优化上面代码
1、使用SpringAMQP优化(基本消息队列)
同上,基本队列模式的模型图:
步骤1:在父工程mq-demo中引入依赖
<!--AMQP依赖,包含RabbitMQ-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
步骤2:声明队列
优化1:【运行ConsumerApplication时】自动声明队列、交换机及其绑定关系(注解方式)
位置:(一般写在消费者模块的)在配置类中,声明队列(也可以声明交换机、以及队列和交换机的绑定关系)
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CommonConfig {
@Bean
public Queue simpleQueue(){
return new Queue("simple.queue1");
}
}
备注:这里需要运行ConsumerApplication才会自动创建该队列
步骤3:publish发送消息
位置:publish模块
3.1application.yml配置
首先配置MQ地址,在publisher服务的application.yml中添加配置:
【application.yml该段配置目的:Spring容器自动创建消息队列的连接】
优化1:配置MQ地址:将冗余的连接参数抽取到yml文件中
spring:
rabbitmq:
host: 10.65.184.58 # 主机名
port: 5672 # 端口
virtual-host: / # 虚拟主机(用户消息隔离)
username: itcast # 用户名
password: 123321 # 密码
Spring容器自动创建消息队列的连接
3.2 消息发送逻辑
1、注入RabbitTemplate
2、(向指定的队列)发送消息
备注:此时该队列还没有声明
RabbitTemplate:
SpringAMQP封装了RabbitTemplate工具,一般用于发送消息 (也可以接收,但是接收一般是用监听器容器去做)
package cn.itcast.mq.listener;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class) //加上这个是为了获得spring环境的上下文支持,不加可能会报错
@SpringBootTest
public class SpringAmqpTest {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void testSimpleQueue() {
// 队列名称
String queueName = "simple.queue1";
// 消息
String message = "hello, spring amqp!";
// (向指定的队列)发送消息
rabbitTemplate.convertAndSend(queueName, message);
}
}
步骤4:消息接收
4.1 application.yml配置
首先配置MQ地址,在consumer服务的application.yml中添加配置:
【application.yml该段配置目的:Spring容器自动创建消息队列的连接】
spring:
rabbitmq:
host: 10.65.184.58 # 主机名
port: 5672 # 端口
virtual-host: / # 虚拟主机
username: itcast # 用户名
password: 123321 # 密码
4.2、消息接收处理逻辑(监听队列)
说明: Spring中自动建立了消息队列的连接、因此我们只需要在Spring定义消息处理的方法,这里,SpringAMQP提供了基于注解的监听器(容器)模式,用于异步接收消息
代码位置:consumer模块下的listener包中
目的:监听指定队列,处理队列中的消息
1、告知Spring容器该方法——@Component
2、该方法需要添加注解:@RabbitListener(queues = “simple.queue1”)表明监听的队列名称
3、该方法定义了处理消息的行为
package cn.itcast.mq.listener;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component//1、告知Spring容器该方法
public class SpringRabbitListener {
@RabbitListener(queues = "simple.queue1")//2、表明监听的队列名称
public void listenSimpleQueueMessage(String msg) throws InterruptedException {
//3、打印消息,模拟处理消息
System.out.println("spring 消费者接收到消息:【" + msg + "】");
}
备注:因为这里使用了Spring容器来接收消息、所以需要打开Application开启Spring,才能接收消息
步骤5:开始测试
发送消息
2、工作消息队列模型WorkQueue
背景:当消息处理比较耗时的时候,可能生产消息的速度会远远大于消息的消费速度。长此以往,消息就会堆积越来越多,无法及时处理。当队列满了,消息无法进入队列,【消息会被丢弃】
此时就可以使用work 模型,多个消费者共同处理消息处理,速度就能大大提高了。
Work queues,也被称为(Task queues),任务模型。简单来说就是让多个消费者绑定到一个队列,共同消费队列中的消息。
工作消息队列模型和基本消息队列基本一致,区别是消费者consumer模块的linstener中,有多个监听方法同时监听同一个队列
多个消费者监听同一队列
代码如下,
模拟2个消费者
@RabbitListener(queues = "simple.queue1")
public void listenWorkQueue1(String msg) throws InterruptedException {
System.out.println("消费者1接收到消息:【" + msg + "】" + LocalTime.now());
Thread.sleep(20);//每接收一次消息就休眠20毫秒、模拟每秒处理50条消息
}
@RabbitListener(queues = "simple.queue1")
public void listenWorkQueue2(String msg) throws InterruptedException {
System.err.println("消费者2........接收到消息:【" + msg + "】" + LocalTime.now());//【err红色打印】
Thread.sleep(200);//每接收一次消息就休眠200毫秒、模拟每秒处理5条消息
}
模拟发布者:
向simple.queue1队列中不停发送消息,模拟消息堆积
/**
* workQueue
* 向simple.queue队列中不停发送消息,模拟消息堆积。
*/
@Test
public void testWorkQueue() throws InterruptedException {
// 队列名称
String queueName = "simple.queue1";
// 消息
String message = "hello, message_";
for (int i = 0; i < 50; i++) {
// 发送消息
rabbitTemplate.convertAndSend(queueName, message + i);
System.out.println("发送成功" + i);
Thread.sleep(20);
}
}
测试
启动ConsumerApplication后,在执行publisher服务中刚刚编写的发送测试方法testWorkQueue。
结果如下:
结果表明:可以看到消费者1很快完成了自己的25条消息。消费者2却在缓慢的处理自己的25条消息。(消费者1不会去帮忙处理消费者2的消息)
这是因为消息预取机制:消息是先平均分配给每个消费者,并没有考虑到消费者的处理能力。这样显然是有问题的,那么,应该如何解决呢?
消费预取限制
我们可以通过修改application.yml文件,设置preFetch这个值,可以控制预取消息的上限,这就是消费预取限制。
每次消费者从消息队列中最多预取1条消息。
spring:
rabbitmq:
host: 10.65.184.58 # 主机名
port: 5672 # 端口
virtual-host: / # 虚拟主机(用户消息隔离)
username: itcast # 用户名
password: 123321 # 密码
listener:
simple:
prefetch: 1 # 每次消费者从消息队列中最多预取1条消息。
再次测试
发布/订阅
背景/需求:【一条消息需要通知多个消费者】但是上面的简单队列和工作队列都无法满足该需求(消息被处理后就消失了——阅后即焚)
发布订阅的模型如图:
1、发布者将消息给交换机【发布者不用跟队列打交道了】
2、交换机将消息给多个队列(这里就完成了同一条消息给多个消费者了)(根据交换机类型的不同,发送给不同的队列)
Fanout Exchange:广播:发送消息给所有绑定了该交换机的队列
Direct Exchange:路由:根据绑定关系中的routingKey发送给指定队列
Topic Exchange:主题:根据绑定关系中的routingKey发送给指定队列(支持通配符)
3、队列(消费者)处理消息
各个角色介绍
在订阅模型中,多了一个exchange角色,而且过程略有变化:
- Publisher:生产者,也就是要发送消息的程序,但是不再发送到队列中,而是发给X(交换机)
- Exchange:交换机,一方面,接收生产者发送的消息。另一方面,知道如何处理消息,例如递交给某个特别队列、递交给所有队列、或是将消息丢弃。到底如何操作,取决于Exchange的类型。【Exchange有以下3种类型:】
Fanout:广播,将消息交给所有绑定到交换机的队列
Direct:定向,把消息交给符合指定routing key 的队列
Topic:通配符,把消息交给符合routing pattern(路由模式) 的队列- Consumer:消费者,与以前一样,订阅队列,没有变化
- Queue:消息队列也与以前一样,接收消息、缓存消息。
备注:Exchange(交换机)只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与Exchange绑定,或者没有符合路由规则的队列,那么消息会丢失!
【发布/订阅操作步骤】:
1、绑定交换机和消息队列(及其路由规则)当消息发送到交换机后,交换机.会自动根据路由规则routingkey将消息发送到队列中(实现同一条消息可同时发送到多个队列)
2、消息发布者 发送消息到交换机的方法(1发送什么消息、2发送到那台交换机、3路由规则routingkey)
3、消费者通过Spring容器监听指定队列中的消息(@RabbitListener(queues = “simple.queue”)+方法),一旦指定的队列中有消息,执行消费者消费消息的方法(处理消息)
备注:步骤1可以单独定义一个配置类+@Bean来完成队列和交换机的绑定,然后(步骤3)消费者通过Spring容器监听指定队列并处理队列里面的消息。
也可以使用@RabbitListener注解将步骤1和3合并完成,后续会进行说明
3、Fanout广播
【广播模式下(即交换机类型声明为Fanout),与广播交换机绑定的队列都可以接收到消息。】
从上图可知,发布/订阅模型和基本/工作消息队列的区别是【新增了exchange交换机以及 需要绑定交换机和队列】
【步骤1】:声明队列、交换机、关系
2个队列、1个交换机(声明为fanout)、2个绑定关系(队列和交互机的绑定关系)
package cn.itcast.mq.config;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FanoutConfig {
/**
* 声明交换机
* @return Fanout类型交换机
*/
@Bean
public FanoutExchange fanoutExchange(){
return new FanoutExchange("itcast.fanout");
}
/**
* 第1个队列
*/
@Bean
public Queue fanoutQueue1(){
return new Queue("fanout.queue1");
}
/**
* 绑定队列1和交换机
*/
@Bean
public Binding bindingQueue1(Queue fanoutQueue1, FanoutExchange fanoutExchange){
return BindingBuilder.bind(fanoutQueue1).to(fanoutExchange);
}
/**
* 第2个队列
*/
@Bean
public Queue fanoutQueue2(){
return new Queue("fanout.queue2");
}
/**
* 绑定队列2和交换机
*/
@Bean
public Binding bindingQueue2(Queue fanoutQueue2, FanoutExchange fanoutExchange){
return BindingBuilder.bind(fanoutQueue2).to(fanoutExchange);
}
}
步骤2:消息发送
在publisher服务的SpringAmqpTest类中添加测试方法:
备注:广播模式下,交换机的routingkey不管写什么都会发送到与之绑定的每一个队列下,因此发送消息时也可以不写routingkey
@Test
public void testFanoutExchange() {
// 名称
String exchangeName = "itcast.fanout";
// 消息
String message = "hello, everyone!";
//往交换机发送消息,参数分别是:交互机名称、RoutingKey(暂时为空)、消息
rabbitTemplate.convertAndSend(exchangeName, "", message);
//rabbitTemplate.convertAndSend(exchangeName, message);
//备注:广播模式下,交换机的routingkey不管写什么都会发送到与之绑定的每一个队列下,因此发送消息时也可以不写routingkey
}
步骤3:消息接收
在consumer服务的SpringRabbitListener中添加两个方法,作为消费者:
@RabbitListener(queues = "fanout.queue1")//表明监听的队列名称
public void listenFanoutQueue1(String msg) throws InterruptedException {
System.out.println("spring 消费者1接收到fanout.queue1消息:【" + msg + "】");
}
@RabbitListener(queues = "fanout.queue2")//表明监听的队列名称
public void listenFanoutQueue2(String msg) throws InterruptedException {
System.out.println("spring 消费者2接收到fanout.queue2消息:【" + msg + "】");
}
测试结果:
使用@RabbitListener注解完成步骤1和步骤3(声明队列、交换机、绑定关系+消息处理)
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "fanout.queue1"),
exchange = @Exchange(name = "itcast.fanout", type = ExchangeTypes.FANOUT))
)
public void listenFanoutQueue1(String msg){
System.out.println("spring 消费者1接收到fanout.queue1消息:【" + msg + "】");
}
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "fanout.queue2"),
exchange = @Exchange(name = "itcast.fanout", type = ExchangeTypes.FANOUT))
)
public void listenFanoutQueue2(String msg) throws InterruptedException {
System.out.println("spring 消费者2接收到fanout.queue2消息:【" + msg + "】");
}
对比一下之前的步骤1+步骤3简单了许多
4、Direct路由
需求:在Fanout模式中,一条消息,会被所有订阅的队列都消费。但是,在某些场景下,我们希望不同的消息被不同的队列消费。这时就要用到Direct类型的Exchange。
在Direct模型下:
该图可完成需求:key为red时,两个消费者都可以收到,blue/yellw则只有指定的消费者可以收到。
与广播模式的区别:
- 队列与交换机的绑定,不能是任意绑定了,而是要指定一个 RoutingKey(路由key)
- 消息的发送方在向Exchange发送消息时,也必须指定消息的 RoutingKey。
- Exchange不再把消息交给每一个绑定的队列,而是交换机根据消息的 Routing Key进行判断,只有队列的Routingkey与消息的 Routing key完全一致,才会接收到消息
- 如果多个队列具有相同的RoutingKey,则与Fanout功能类似(即广播)
实现步骤:
1、监听+绑定关系
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "direct.queue1"),
exchange = @Exchange(name = "itcast.direct", type = ExchangeTypes.DIRECT),
key = {"red", "blue"}
))
public void listenDirectQueue1(String msg){
System.out.println("消费者接收到direct.queue1的消息:【" + msg + "】");
}
//绑定队列direct.queue2和交换机itcast.direct、类型为DIRECT
//当发送到交换机的消息的key为red和yellw时,采取下面的消费者处理方法
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "direct.queue2"),
exchange = @Exchange(name = "itcast.direct", type = ExchangeTypes.DIRECT),
key = {"red", "yellow"}
))
public void listenDirectQueue2(String msg){
System.out.println("消费者接收到direct.queue2的消息:【" + msg + "】");
}
2、发送消息(携带routingkey)
发送消息需要携带routingkey
@Test
public void testSendDirectExchange() {
// 交换机名称
String exchangeName = "itcast.direct";
// 消息
String message = "红色警报!日本乱排核废水,导致海洋生物变异,惊现哥斯拉!";
// 发送消息
rabbitTemplate.convertAndSend(exchangeName, "red", message);
}
5、Topic话题
Topic
类型Exchange
和Direct类型的类似,区别是可以让队列在绑定Routing key
的时候使用通配符;
使用通配符的好处,我们可以从下图看出
假如使用Direct与queue1绑定的key为:china.news、china.whether;(2个)
其他同理,总共需要8个,这还是只有两个类型/两个国家的情况下,假如我们有10多个类型,每次都要写10多个routingkey就会很麻烦;
使用Topic类型的交换机,我们可以指定queue1和交换机绑定的key为china.#;
通配符规则:
#
:匹配一个或多个词
*
:匹配1个词
举例:
item.#
:能够匹配item.spu.insert
或者item.spu
item.*
:只能匹配item.spu
操作步骤:
1、绑定关系+监听
(这里只写第一个队列和第四个队列)
(修改key和交换机类型)
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "topic.queue1"),
exchange = @Exchange(name = "itcast.topic", type = ExchangeTypes.TOPIC),
key = "china.#"
))
public void listenTopicQueue1(String msg){
System.out.println("消费者接收到topic.queue1的消息:【" + msg + "】");
}
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "topic.queue4"),
exchange = @Exchange(name = "itcast.topic", type = ExchangeTypes.TOPIC),
key = "#.news"
))
public void listenTopicQueue2(String msg){
System.out.println("消费者接收到topic.queue2的消息:【" + msg + "】");
}
发送消息
/**
* topicExchange
*/
@Test
public void testSendTopicExchange() {
// 交换机名称
String exchangeName = "itcast.topic";
// 消息
String message = "喜报!孙悟空大战哥斯拉,胜!";
// 发送消息
rabbitTemplate.convertAndSend(exchangeName, "china.news", message);
}
消息转换器
查看我们测试时rabbitTemplate.convertAndSend()的消息,方法中的消息的类型是Object,所以可以是任意类型,那么也可以是对象。
但是,Spring会把你【发送的消息】【序列化为字节】发送给MQ,
【接收消息】的时候,还会把【字节反序列化为Java对象】。
默认情况下Spring采用的序列化方式是JDK序列化。JDK序列化存在下列问题:
- 有安全漏洞(注入问题)
- 数据体积过大,导致 可读性差且数据传输性能差
下面开始测试默认转换器(JDK序列化)
1、在spring中声明队列(位置:consumer模块-config)
这里为了保证消息不被消费
(便于在控制台的队列查看消息被序列化的格式长什么样)
(Spring容器监听方式会立刻消费掉消息)
所以在config中声明队列(队列存储消息,没有队列就没有存储消息来查看)
@Bean
public Queue objectQueue(){
return new Queue("object.queue");
}
声明完队列记得启动consumerApplication(Spring帮你声明队列)
2、这里直接发送到队列中(简单队列模型)
我们修改消息发送的代码,发送一个Map对象:
/**
* 测试消息为对象类型,
*/
@Test
public void testSendMap() throws InterruptedException {
// 准备消息
Map<String,Object> msg = new HashMap<>();
msg.put("name", "Jack");
msg.put("age", 21);
// 发送消息
rabbitTemplate.convertAndSend("object.queue", msg);
}
发送消息后查看RabbitMQ控制台:
没有按照期望的显示出一个对象,而是消息被JDK序列化为字节
优化:配置JSON转换器(publisher中)
发送消息时,我们不想要JDK序列化方式。显然,发送消息时,JDK序列化方式并不合适。我们希望消息体的体积更小、可读性更高,因此可以使用JSON方式来做序列化和反序列化。
1、在publisher和consumer两个服务中都引入依赖:(也可以直接引入父工程–去掉版本)
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.9.10</version>
</dependency>
2、配置消息转换器
在【publisher启动类】中添加一个Bean即可:
//MessageConverter 导入的是amqp包下的
//Jackson2JsonMessageConverter这个类里面又依赖了jackson,所以要引入jackson的依赖
@Bean
public MessageConverter jsonMessageConverter(){
return new Jackson2JsonMessageConverter();
}
再次发送数据,结果显示如下,完成JSON方式来做序列化和反序列化。
消息队列专栏文章:
RabbitMQ(二):RabbitMQ的安装(Linux、基于docker安装)及其插件安装
RabbitMQ(三):RabbitMQ快速入门(SpringBoot)
文章整理自:黑马教学视频