SpringBoot整合RabbitMQ
RabbitMQ简介
RabbitMQ是由erlang开发的AMQP(Advanced Message Queuing Protocol)的开源实现。
核心概念
Message
消息,消息是不具名的,它由消息头和消息体组成。消息体是不透明的,而消息头则由一系列的可选属性组成,这些属性包括routing-key(路由键)、priority(相对于其他消息的优先权)、delivery-mode(指出该消息可能需要持久性存储)等。
Publisher
消息的生产者,也是一个向交换器发布消息的客户端应用程序。
Exchange
交换器,用来接收生产者发送的消息并将这些消息路由给服务器中的队列。
Exchange有4种类型:direct(默认),fanout, topic, 和headers,不同类型的Exchange转发消息的策略有所区别
Queue
消息队列,用来保存消息直到发送给消费者。它是消息的容器,也是消息的终点。一个消息可投入一个或多个队列。消息一直在队列里面,等待消费者连接到这个队列将其取走。
Binding
绑定,用于消息队列和交换器之间的关联。一个绑定就是基于路由键将交换器和消息队列连接起来的路由规则,所以可以将交换器理解成一个由绑定构成的路由表。
Exchange 和Queue的绑定可以是多对多的关系。
Connection
网络连接,比如一个TCP连接。
Channel
信道,多路复用连接中的一条独立的双向数据流通道。信道是建立在真实的TCP连接内的虚拟连接,AMQP 命令都是通过信道发出去的,不管是发布消息、订阅队列还是接收消息,这些动作都是通过信道完成。因为对于操作系统来说建立和销毁 TCP 都是非常昂贵的开销,所以引入了信道的概念,以复用一条 TCP 连接。
Consumer
消息的消费者,表示一个从消息队列中取得消息的客户端应用程序。
Virtual Host
虚拟主机,表示一批交换器、消息队列和相关对象。虚拟主机是共享相同的身份认证和加密环境的独立服务器域。每个 vhost 本质上就是一个 mini 版的 RabbitMQ 服务器,拥有自己的队列、交换器、绑定和权限机制。vhost 是 AMQP 概念的基础,必须在连接时指定,RabbitMQ 默认的 vhost 是 “/”。
Broker
表示消息队列服务器实体
运行机制
消息的生产者将消息发布到Exchange上,根据Exchange的类型以及Binding关系,决定Exchange上的消息发送到哪个队列。
Exchange类型
direct
如果消息的路由键与Binding中的bindingKey完全一致,就发送到对应的队列中。
fanout
消息会广播到所有binding的队列中
topic
交换机通过模式匹配分配消息的路由键属性,将路由键和某个模式进行匹配,此时队列需要绑定到一个模式上。它将路由键和绑定键的字符串切分成单词,这些单词之间用点隔开。它同样也会识别两个通配符:符号“#”和符号“”。#匹配0个或多个单词,*****匹配一个单词。
如binding中有模式dog.#、*.msg,如果给交换机发送一个路由键为dog.msg的消息,那么前面两种模式的队列都会收到这个dog.msg路由键的消息。
headers
匹配 AMQP 消息的 header 而不是路由键, headers 交换器和 direct 交换器的匹配完全一致,但性能差很多,目前几乎用不到了
整合RabbitMQ
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
RabbitMQ服务器连接
(Application.yml)rabbitMQ的默认账号密码是guest/guest
spring:
rabbitmq:
host: 192.168.37.100
port: 5672
username: guest
password: guest
测试使用
初步使用RabbitTemplate和RabbitAdmin
package org.fall;
import org.fall.entity.Person;
import org.junit.jupiter.api.Test;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SpringbootAmqpApplicationTests {
// mq的消息发送和处理组件
@Autowired
RabbitTemplate rabbitTemplate;
// mq的管理组件
@Autowired
RabbitAdmin rabbitAdmin;
/**
* 使用rabbitAdmin进行交换器、队列、绑定关系等的初始化
*/
@Test
void contextLoads() {
// 创建交换器
// 传入要创建的交换器的名字
rabbitAdmin.declareExchange(new DirectExchange("exchange.direct"));
rabbitAdmin.declareExchange(new FanoutExchange("exchange.fanout"));
rabbitAdmin.declareExchange(new TopicExchange("exchange.topic"));
/* 创建队列:
fall
fall.other
fall.msg
learn.msg
Queue的构造器中传入要创建的队列的名字
*/
rabbitAdmin.declareQueue(new Queue("fall"));
rabbitAdmin.declareQueue(new Queue("fall.other"));
rabbitAdmin.declareQueue(new Queue("fall.msg"));
rabbitAdmin.declareQueue(new Queue("learn.msg"));
// 绑定队列与交换器
/**
* Binding构造器的源码:
* public Binding(
* String destination, // 绑定的目标
* DestinationType destinationType, // 绑定的种类(QUEUE/EXCHANGE)
* String exchange, // 绑定的交换器的名字
* String routingKey, // 路由键
* @Nullable Map<String, Object> arguments)// 传入的参数 ,可以为null
* {
* // 处理的代码省略
* }
*/
rabbitAdmin.declareBinding(new Binding("fall", Binding.DestinationType.QUEUE, "exchange.direct", "fall",null));
rabbitAdmin.declareBinding(new Binding("fall.other", Binding.DestinationType.QUEUE, "exchange.direct", "fall.other",null));
rabbitAdmin.declareBinding(new Binding("fall.msg", Binding.DestinationType.QUEUE, "exchange.direct", "fall.msg",null));
rabbitAdmin.declareBinding(new Binding("learn.msg", Binding.DestinationType.QUEUE, "exchange.direct", "learn.msg",null));
rabbitAdmin.declareBinding(new Binding("fall", Binding.DestinationType.QUEUE, "exchange.fanout", "fall",null));
rabbitAdmin.declareBinding(new Binding("fall.other", Binding.DestinationType.QUEUE, "exchange.fanout", "fall.other",null));
rabbitAdmin.declareBinding(new Binding("fall.msg", Binding.DestinationType.QUEUE, "exchange.fanout", "fall.msg",null));
rabbitAdmin.declareBinding(new Binding("learn.msg", Binding.DestinationType.QUEUE, "exchange.fanout", "learn.msg",null));
rabbitAdmin.declareBinding(new Binding("fall", Binding.DestinationType.QUEUE, "exchange.topic", "fall",null));
rabbitAdmin.declareBinding(new Binding("fall.other", Binding.DestinationType.QUEUE, "exchange.topic", "fall.#",null));
rabbitAdmin.declareBinding(new Binding("fall.msg", Binding.DestinationType.QUEUE, "exchange.topic", "fall.#",null));
rabbitAdmin.declareBinding(new Binding("fall.msg", Binding.DestinationType.QUEUE, "exchange.topic", "*.msg",null));
rabbitAdmin.declareBinding(new Binding("learn.msg", Binding.DestinationType.QUEUE, "exchange.topic", "*.msg",null));
}
/**
* 测试使用RabbitTemplate发送简单数据(如字符串)
*/
@Test
public void testRabbitTemplate() {
// 向RabbitMQ发送消息的几种方法:
// 1、直接使用send方法,需要自己构造Message
//rabbitTemplate.send(exchange,routingKey,message);
// 2、 使用convertAndSend,只要传入要发送的对象,会自动序列化后发送给mq
rabbitTemplate.convertAndSend("exchange.direct", "fall", "hello fall~");
}
/**
* 测试发送对象数据
* 前提要求:要发送的对象的类实现了序列化接口
* public void convertAndSend(String exchange, String routingKey, Object object)
* exchange :发送目标交换器的名字
* routingKey :消息的路由键
* object :发送的消息内容
*/
@Test
public void testRabbit02() {
rabbitTemplate.convertAndSend("exchange.topic", "fall.xx",new Person(1,"lis"));
System.out.println("finish");
}
/**
* 从指定的队列中获得队列中的消息
*/
@Test
public void testRabbit03() {
Object o = rabbitTemplate.receiveAndConvert("fall.msg"); // 传入队列的名字
System.out.println("class: " + o.getClass());
System.out.println("toString: " + o);
}
}
使用自定义MessageConverter
@Configuration
public class MyRabbitMQConfig {
// 配置自定义的MessageConverter,代替默认的,以达到自动将对象在放入消息队列时转换成JSON格式的目的
@Bean
public MessageConverter messageConverter() {
return new Jackson2JsonMessageConverter();
}
}
只要将自定义的MessageConverter加入IOC容器后,默认的就会失效,转而使用自定义的,达到在放入消息队列的时候对象呈现JSON格式的目的。
@RabbitListener简单使用
使用RabbitListener必须先在主力启动类上开启Rabbit功能(@EnableRabbit
):
@SpringBootApplication
@EnableRabbit // 开启RabbitMQ消息队列功能
public class SpringbootAmqpApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootAmqpApplication.class, args);
}
}
在业务代码中通过@RabbitListener注解,可以实现在对应消息队列中传入数据的时候,触发标注了的方法。
并且在该方法触发的同时,队列中的消息也会被取出,而不是继续存放在队列中。
@Service
public class MyService {
// RabbitListener注解在方法上,可以监听队列,当该队列有消息进入的时候,会触发该方法
@RabbitListener(queues = "fall.msg")
public void listener() {
System.out.println("fall.msg 有新消息进入...");
}
// 在方法的入参加Message对象,Spring会在监听到的同时自动注入message对象,在方法中可以查看message的信息
@RabbitListener(queues = "fall.other")
public void listenerMessage(Message message) {
MessageProperties messageProperties = message.getMessageProperties();
System.out.println(messageProperties);
// 通过getBody()方法获得byte数组格式的消息,通过new String(Byte[] bytes)获得JSON格式的对象。
byte[] bytes = message.getBody();
System.out.println(new String(bytes));
}
}