1、MQ概念
MQ:消息队列,底层为先进先出数据结构的消息服务器;
MQ作用:实现两个系统中的数据传输,消息即为传输的数据。
- 系统间传输数据的传统方式
- 系统间传输数据的队列方式
传统方式:同步,能够立刻拿到被调用接口处理后的数据;
消息队列(MQ):异步,效率高,无法拿到消费者处理后的数据
MQ的应用场景:系统解耦,流量消峰,数据分发
2、RabbitMQ工作原理
- Producer(生产者):负责进行消息的发送
- Consumer(消费者):负责从MQ中获取消息进行消费
- Broker(代理):RabbitMQ的服务端
- Exchange(交换机):不存储消息,只转发消息,把消息转发给指定队列
- Queue(队列):负责存储消息
3、使用
(1)环境搭建
- 使用docker拉取镜像,本次使用版本为3.8
docker pull rabbitmq:3.8-management
- 使用docker安装rabbitMQ容器,设置端口,挂载点等
docker run -d --name rabbit01 -e RABBITMQ_DEFAULT_USER=admin \
-e RABBITMQ_DEFAULT_PASS=admin \
-p 15672:15672 -p 5672:5672 \
--hostname rabbit01 --restart=always \
-v rabbit01_data:/var/lib/rabbitmq \
-v rabbit01_conf:/etc/rabbitmq \
rabbitmq:3.8-management
- 使用ip和端口查看rabbitMQ的后台管理系统
(2)编写代码
2.1 简单队列模型的实现
使用重点:注入RabbitTemplate发消息,给方法添加@RabbitListener(queues="simple_queue")注解收消息
- 创建Maven工程,导入springboot与mq的启动项与springboot与junit的启动项
<!-- 指定父工程 -->
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.3.6.RELEASE</version>
</parent>
<dependencies>
<!-- spring boot和rabbitmq整合的时候所需要的起步依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!-- spring boot和junit整合的起步依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
- 创建producer(生产者)子模块,在子模块中创建Application启动器
@SpringBootApplication
public class RabbitmqProducerApplication {
public static void main(String[] args) {
SpringApplication.run(RabbitmqProducerApplication.class,args);
}
}
- 在yml文件中配置rabbitMQ的参数
spring:
rabbitmq:
host: 192.168.126.137
port: 5672
username: admin
password: admin
virtual-host: /
- 在producer模块中创建测试方法,注入rabbitTemplate依赖,调用其方法发送消息
@SpringBootTest
public class Producer01 {
@Autowired
RabbitTemplate rabbitTemplate;
//生产者1,测试简单模式
@Test
public void test01(){
rabbitTemplate.convertAndSend("simple_queue","测试消息1");
}
}
- 创建consumer(消费者)子模块;在其中创建Application启动器
@SpringBootApplication
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class,args);
}
}
- 配置参数
spring:
rabbitmq:
host: 192.168.126.137
port: 5672
username: admin
password: admin
virtual-host: /
- 在子模块中创建listener01包,并创建一个监听类,类上加@Component注解,创建方法并在方法上加@RabbitListener注解,方法中带参数Message,注解中带参数(queues=“simple_queue”)
@Component
public class Listen01 {
// 创建监听器用来消费消息队列中的消息
// 简单队列消费者
@RabbitListener(queues = "simple_queue01")
public void consumer01(Message message){
byte[] body = message.getBody();
String s = new String(body);
System.out.println("msg------>"+s);
}
}
2.2 工作队列模型的实现
使用重点:降低生产速度,提高消费速度;注入RabbitTemplate发消息,给方法添加@RabbitListener(queues="simple_queue")注解收消息
- 创建生产模块方法,使用循环发送多个消息
//生产者2,测试工作模式
@Test
public void test02(){
for (int i = 0; i < 10; i++) {
rabbitTemplate.convertAndSend("simple_queue01","测试消息2"+i);
}
}
- 在消费模块中再创建一个listener02包,并创建消费方法
@Component
public class Listen02 {
// 创建监听器用来消费消息队列中的消息
// 简单队列消费者
@RabbitListener(queues = "simple_queue01")
public void consumer01(Message message){
byte[] body = message.getBody();
String s = new String(body);
System.out.println("consumer-02------>"+s);
}
}
2.3 发布订阅模型
使用重点:多了一个exchange(交换机)的角色,需要手动指定消息转发的队列;使用的交换机为Fanout交换机,自动将生产者的消息发送给所有与交换机绑定的队列;
Fanout交换机
特点:将消息转发给所有绑定的队列,创建方法为ExchangeBuilder,QueueBuilder,BindingBuilder
- 在生产者中创建配置类
@Configuration
public class ProducerConfiguration {
// 创建交换机
@Bean
public Exchange fanoutExchange(){
Exchange fanoutexchange = ExchangeBuilder.fanoutExchange("fanout_exchange")
.durable(true).build();
return fanoutexchange;
}
// 创建队列01
@Bean
public Queue queue01(){
Queue queue01 = QueueBuilder.durable("queue01").build();
return queue01;
}
// 创建队列02
@Bean
public Queue queue02(){
Queue queue02 = QueueBuilder.durable("queue02").build();
return queue02;
}
// 创建绑定1
@Bean
public Binding binding01(){
Binding binding01 = BindingBuilder.bind(queue01()).to(fanoutExchange()).with("").noargs();
return binding01;
}
// 创建绑定2
@Bean
public Binding binding02(){
Binding binding02 = BindingBuilder.bind(queue02()).to(fanoutExchange()).with("").noargs();
return binding02;
}
}
- 在消费者中创建两个监听类消费消息
@Component
public class Listen01 {
@RabbitListener(queues = "queue01")
public void consumer01(Message message){
byte[] body = message.getBody();
String s = new String(body);
System.out.println("consumer01---->"+s);
}
}
@Component
public class Listen02 {
@RabbitListener(queues = "queue02")
public void consumer02(Message message){
byte[] body = message.getBody();
String s = new String(body);
System.out.println("consumer02---->"+s);
}
}
- 在消费者中创建测试方法给交换机发送消息
//生产者3,测试订阅模式
@Test
public void test03(){
rabbitTemplate.convertAndSend("fanout_exchange","","你好fanout交换机!!");
}
2.4 路由模型
重点:交换机将消息通过路由(字符串匹配)发送到响应队列
Direct交换机
特点:将消息转发给指定路由的队列
- 在生产者模块中创建配置类
@Configuration
public class DirectConfigurarion {
// 创建交换机
@Bean
public Exchange directExchange(){
Exchange directExchange = ExchangeBuilder.directExchange("direct_exchange").durable(true).build();
return directExchange;
}
// 创建队列03
@Bean
public Queue queue03(){
Queue queue03 = QueueBuilder.durable("queue03").build();
return queue03;
}
// 创建队列04
@Bean
public Queue queue04(){
Queue queue04 = QueueBuilder.durable("queue04").build();
return queue04;
}
// 创建绑定error
@Bean
public Binding binding03(){
Binding binding03 = BindingBuilder.bind(queue03()).to(directExchange()).with("error").noargs();
return binding03;
}
// 创建绑定error
@Bean
public Binding binding04(){
Binding binding04 = BindingBuilder.bind(queue04()).to(directExchange()).with("error").noargs();
return binding04;
}
// 创建绑定info
@Bean
public Binding binding05(){
Binding binding04 = BindingBuilder.bind(queue04()).to(directExchange()).with("info").noargs();
return binding04;
}
}
- 在消费者模块创建监听类
@Component
public class Listen03 {
@RabbitListener(queues = "queue03")
public void consumer03(Message message){
byte[] body = message.getBody();
String s = new String(body);
System.out.println("consumer03---->"+s);
}
}
- 在生产者中创建测试方法
//生产者4,测试路由模式
@Test
public void test04(){
rabbitTemplate.convertAndSend("direct_exchange","error","测试error消息,同时发送给03和04");
rabbitTemplate.convertAndSend("direct_exchange","info","测试info消息,只发送给04");
}
2.5 主题模型
重点:动态路由,在绑定时可以使用通配符,路由路径分为*和#
Topic交换机
特点:路径中通配符为#,可以匹配0个或多个词;路径中通配符为*,可以只能匹配1个词
- 在生产模块中创建配置类
@Configuration
public class TopicConfiguration {
// 创建交换机
@Bean
public Exchange topicExchange(){
Exchange topicExchange = ExchangeBuilder.topicExchange("topic_exchange").durable(true).build();
return topicExchange;
}
// 创建队列
@Bean
public Queue queue05(){
Queue queue05 = QueueBuilder.durable("queue05").build();
return queue05;
}
// 创建队列
@Bean
public Queue queue06(){
Queue queue06 = QueueBuilder.durable("queue06").build();
return queue06;
}
// 创建绑定05
@Bean
public Binding binding5(){
Binding binding05 = BindingBuilder.bind(queue05())
.to(topicExchange()).with("*.orange.*").noargs();
return binding05;
}
// 创建绑定06
@Bean
public Binding binding6(){
Binding binding06 = BindingBuilder.bind(queue06())
.to(topicExchange()).with("lazy.#").noargs();
return binding06;
}
}
- 在消费模块中创建监听器
@Component
public class Listen05 {
@RabbitListener(queues = "queue05")
public void consumer05(Message message){
byte[] body = message.getBody();
String s = new String(body);
System.out.println("consumer05---->"+s);
}
}
@Component
public class Listen06 {
@RabbitListener(queues = "queue06")
public void consumer06(Message message){
byte[] body = message.getBody();
String s = new String(body);
System.out.println("consumer06---->"+s);
}
}
- 在生产模块中创建测试方法
//生产者5,测试主题模式
@Test
public void test05(){
rabbitTemplate.convertAndSend("topic_exchange","a.orange.b","测试*.orange.*匹配一个路径");
rabbitTemplate.convertAndSend("topic_exchange","orange.a.b","测试*.orange.*匹配零个路径");
rabbitTemplate.convertAndSend("topic_exchange","lazy.a","测试lazy.*匹配一个路径");
rabbitTemplate.convertAndSend("topic_exchange","lazy.a.b","测试lazy.*匹配两个路径");
rabbitTemplate.convertAndSend("topic_exchange","lazy","测试lazy.*匹配零个路径");
}
4、注解方式创建配置
重点:在使用时常规操作较为繁琐,需要创建配置类,而@RabbitListener注解则可以将步骤简化;
注解操作为:生产者发送消息,创建监听器,在监听器注解中配置参数,主要参数为@RabbitListener(bindings (value,exchange,key))
- 创建监听类,在注解中配置参数
@Component
public class Listen07 {
// 使用注解直接监听消息,以路由模式为例
// 监听器07
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "queue07",durable = "true"),
exchange = @Exchange(name = "direct02_exchange",durable = "true",type = ExchangeTypes.DIRECT),
key = {"error"}
))
public void listen07(Message message){
byte[] body = message.getBody();
String s = new String(body);
System.out.println("consumer07----->"+s);
}
// 监听器08
@RabbitListener(bindings = @QueueBinding(
value = @Queue(name = "queue08",durable = "true"),
exchange = @Exchange(name = "direct02_exchange"),
key = {"error","info"}
))
public void listen08(Message message){
byte[] body = message.getBody();
String s = new String(body);
System.out.println("consumer08----->"+s);
}
}
- 创建测试类,模拟生产者发送消息
生产者6,测试使用注解监听消息,以订阅模式为例
@Test
public void test06(){
rabbitTemplate.convertAndSend("direct02_exchange","error","测试error消息,7和8都要听到");
rabbitTemplate.convertAndSend("direct02_exchange","info","测试info消息,8都要听到");
}
5、总结
重点: (1)RabbitMQ概念:是一个底层数据结构为队列的服务器;
(2)RabbitMQ作用:实现系统之间的数据传输;
(3)RabbitMQ应用:流量消峰,系统解耦,分发数据
(4)RabbitMQ使用:熟练掌握以上五种模式的操作,熟练掌握注解的使用。