一、RabbitMQ的工作模式本文在预先部署过了RabbitMQ及其管理界面的情境下
先介绍一下RabbitMQ的工作模式,RabbitMQ共五种工作模式:
-
简单模式:就是一个生产者一个消费者,中间通过一个队列直接连接。
-
work工作模式:就是有多个消费者消费队列里的消息,队列里的消息只能被一个消费者消费。这样多部署几个消费者,就可以缓解压力,比如过年的时候抢票,抢票成功后会给你发短信,这个时候就可以把发短信的任务放入队列里,然后有多个发短信的服务来处理队列里的任务。(其他场景:抢红包,抢票等)
-
pub/sub发布订阅模式:上面两种模式生产者的消息只能被一个消费者消费,不符合某些实际场景。需要用到一个Exchange交换机角色来帮助我们把消息发给所有订阅我们的服务商。每个消费者都能消费一整套完整的消息。
-
Routing路由模式:路由模式就是交换机并不是给所有订阅他的队列发送消息了,而是根据路由键来确定应该给哪个队列发送消息,队列和交换机绑定的时候需要通过路由键,而生产者发送消息的时候也需要指定路由键,这样就可以确定给哪个队列发送消息了。
-
Topics主题模式:主题模式就相当于模糊匹配,假如我想给姓张的发送消息,那么我就可以通过主题模式给所有姓张的发送消息。
1.4 两个子项目的resource下建立springboot的配置文件:application.yml文件,内容如下:4.0.0 com.microsoft rabbitmq-demo pom 1.0-SNAPSHOT rabbit-producer rabbit-consumer org.springframework.boot spring-boot-starter-parent 2.5.3 11 11 org.springframework.boot spring-boot-starter-amqp org.springframework.boot spring-boot-starter-test org.springframework.amqp spring-rabbit-test junit junit org.springframework.boot spring-boot-maven-plugin
spring:
rabbitmq:
host: (IP)
port: 5672
virtual-host:
username:
password:
1.5 在两个子项目下建立两个启动类
package com.microsoft;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class RabbitProducerApplication {
public static void main(String[] args) {
SpringApplication.run(RabbitProducerApplication.class, args);
}
}
package com.microsoft;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class RabbitConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(RabbitConsumerApplication.class, args);
}
}
1.6 监听器
package com.microsoft.consumer;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@RabbitListener(queues = "test.rabbit.queue") //指定监听的队列名
public class RabbitConsumerOne {
@RabbitHandler //消息接收处理
public void showMsg(String message) { //得到我们producer中发送的Object数据,此处可根据传过来的类型来选择接收,否则抛出异常
System.out.println("test.rabbit.queue收到的消息内容为:" + message);
}
}
1.7 生产者
package com.microsoft.producer;
import com.microsoft.RabbitProducerApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = RabbitProducerApplication.class)
public class RabbitProducerOne {
@Resource
private RabbitTemplate rabbitTemplate;
@Test
public void sendQueue(){
System.out.println("开始向队列中发送一条消息!");
// 参数1:管理中的队列名 参数2:发送的消息
rabbitTemplate.convertAndSend("test.rabbit.queue","message:这是一条消息!");
System.out.println("消息发送完毕!");
}
}
1.8 启动监听器的启动类,启动生产者的测试方法
2022-05-07 10:08:31.800 INFO 6508 --- [ main] com.microsoft.RabbitConsumerApplication : Started RabbitConsumerApplication in 1.556 seconds (JVM running for 2.828) test.rabbit.queue收到的消息内容为:message:这是一条消息!2. 发布订阅模式代码示例 2.1 到RabbitMQ的管理界面新增一个路由交换机(Exchange) 2.2 新建完路由之后,需要再新建几个队列 2.3 把新建的路由和新建的队列绑定
出现如图界面进行绑定
绑定完成,可进行代码测试。
2.4 监听器package com.microsoft.consumer;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component // 需要注入到Spring容器中
@RabbitListener(queues = "pubsubqueue.one") // 指定监听的队列名
public class SubConsumerOne {
@RabbitHandler// 消息接收处理
public void showMsg(String message) { // 得到我们producer中发送的Object数据,此处可根据传过来的类型来选择接收,否则抛出异常
System.out.println("pubsubqueue.one收到的消息内容为:" + message);
}
}
package com.microsoft.consumer;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component // 需要注入到Spring容器中
@RabbitListener(queues = "pubsubqueue.two") // 指定监听的队列名
public class SubConsumerTwo {
@RabbitHandler// 消息接收处理
public void showMsg(String message) {
System.out.println("pubsubqueue.two收到的消息内容为:" + message);
}
}
package com.microsoft.consumer;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component // 需要注入到Spring容器中
@RabbitListener(queues = "pubsubqueue.three") // 指定监听的队列名
public class SubConsumerThree {
@RabbitHandler// 消息接收处理
public void showMsg(String message) {
System.out.println("pubsubqueue.three收到的消息内容为:" + message);
}
}
2.5 生产者
package com.microsoft.producer;
import com.microsoft.RabbitProducerApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = RabbitProducerApplication.class)
public class RabbitProducerTwo {
@Resource
private RabbitTemplate rabbitTemplate;
// 广播的形式发送,同一个路由下的所有队列都能接收到消息
@Test
public void sendFanout() {
System.out.println("开始向路由发送消息(路由下的所有Queue都能收到该消息)");
// 参数1:路由名 参数2:可有可无 参数3:发送的消息内容
rabbitTemplate.convertAndSend("test-myroute", "", "这是一条所有消费者都能收到的消息!");
System.out.println("消息发送成功!");
}
}
2.6 启动监听器的启动类,启动生产者的测试方法
2022-05-07 11:26:07.844 INFO 14428 --- [ main] com.microsoft.RabbitConsumerApplication : Started RabbitConsumerApplication in 1.058 seconds (JVM running for 1.755) pubsubqueue.two收到的消息内容为:这是一条所有消费者都能收到的消息! pubsubqueue.three收到的消息内容为:这是一条所有消费者都能收到的消息! pubsubqueue.one收到的消息内容为:这是一条所有消费者都能收到的消息!3. 路由模式和主题模式代码示例
路由模式是精确匹配,主题模式是模糊匹配
3.1 新建一个用来发送主题的路由[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AxfXDVb9-1651907937956)(https://gitee.com/zihanstudy/blog-pic/raw/master/img/image-20220507114107716.png)]
3.2 路由新建完之后,新建3个队列,用来接收发布的topic 3.3 把新建的队列和路由进行绑定3.4 监听器RabbitMQ主题模式(Topic Exchange)通配符的匹配规则
- 每条消息会被发送到所有符合路由规则的key对应的队列
- 可以使用"*“和”#"两种通配符
- *代表匹配任意一个关键词,#代表匹配一个或多个关键词
- 通配符和普通字符之间需要使用"."隔开
@Component
@RabbitListener(queues = "topicqueue.one")
public class TopicConsumerOne {
@RabbitHandler
public void showMsg(String message) {
System.out.println("topicqueue.one收到的消息内容为:" + message);
}
}
@Component
@RabbitListener(queues = "topicqueue.two")
public class TopicConsumerTwo {
@RabbitHandler
public void showMsg(String message) {
System.out.println("topicqueue.two收到的消息内容为:" + message);
}
}
@Component
@RabbitListener(queues = "topicqueue.three")
public class TopicConsumerThree {
@RabbitHandler
public void showMsg(String message) {
System.out.println("topicqueue.three收到的消息内容为:" + message);
}
@RabbitHandler
public void showMsg(Map map){
System.out.println("topicqueue.three收到的(map)消息如下:" + map);
}
@RabbitHandler
public void showMsg(List list){
System.out.println("topicqueue.three收到的(list)消息如下:" + list);
}
}
3.5 测试1
@Test
public void sendTopic1(){
System.out.println("开始向路由中发送消息!");
// 参数1:路由器名 参数2:routingKey,类似于发送的规则名
rabbitTemplate.convertAndSend("test-mytopic","java.py","这是一条java.py消息");
}
测试结果:
2022-05-07 14:45:24.758 INFO 15548 --- [ main] com.microsoft.RabbitConsumerApplication : Started RabbitConsumerApplication in 1.107 seconds (JVM running for 2.316) topicqueue.two收到的消息内容为:这是一条java.py消息 topicqueue.one收到的消息内容为:这是一条java.py消息 topicqueue.three收到的消息内容为:这是一条java.py消息
因为3个队列都符合了规则,所以都能消费到消息
3.6 测试2 @Test
public void sendTopic2(){
System.out.println("开始向路由中发送消息!");
rabbitTemplate.convertAndSend("test-mytopic","nlp.py","这是一条nlp.py消息");
rabbitTemplate.convertAndSend("test-mytopic","ocr.py","这是一条ocr.py消息");
}
测试结果:
topicqueue.two收到的消息内容为:这是一条nlp.py消息 topicqueue.two收到的消息内容为:这是一条ocr.py消息
此处只有topicqueue.two能接收到消息,因为topicqueue.two符合以 .py 结尾
3.7 测试3 @Test
public void sendTopic3(){
// 1.准备发送的数据
Map map = new HashMap();
map.put(1,"a");
map.put(2,"b");
List list = new ArrayList();
list.add("qq");
list.add("ww");
list.add("SS");
System.out.println("开始向路由中发送消息!");
// 2.开始发送消息(发送了2条)
// 2.1 发送的数据为map类型
rabbitTemplate.convertAndSend("test-mytopic", "java.cpp", map);
// 2.2 发送的数据为list类型
rabbitTemplate.convertAndSend("test-mytopic", "java.cpp", list);
}
测试结果:
topicqueue.three收到的(map)消息如下:{1=a, 2=b}
topicqueue.three收到的(list)消息如下:[qq, ww, SS]
此处只有topicqueue.three能够收到消息,且被指定参数类型的监听器所消费。



