一、Topic交换机
主题模式需要用到Topic类型的交换机
Topic交换机路由匹配规则:
* :精确匹配一个单词,必须要有值
# :匹配0个或者多个单词
举例:
如上图的主题模式中,Q1队列绑定*.orange.*路由键,Q2绑定*.orange.#路由键
1、如果生产者发送路由为one.orange.two的消息,则C1、C2都可以收到消息
2、如果生产者发送路由为orange.one的消息,则都不能收到消息
3、如果生产者发送路由为one.orange的消息,则只有C2可以收到消息
二、实战
新建两个SpringBoot项目(版本号2.3.4.RELEASE):rabbitmq-provider、rabbitmq-consumer
本机启动RabbitMQ Server,本篇采用Docker来启动,大家也可以安装包启动,两种即可,这里不做赘述。
本篇对上述三种不同路由键做测试。
2.1、rabbitmq-consumer
引入RabbitMQ依赖:pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
配置文件:application.properties
server.port=9002
server.servlet.context-path=/consumer
spring.application.name=rabbitmq-consumer
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
新建一个常量类:RabbitMqConstant.java
public interface RabbitMqConstant {
String TOPIC_EXCHANGE_TEST = "topic.exchange.test";
String TOPIC_QUEUE_TEST1 = "topic.queue.test1";
String TOPIC_QUEUE_TEST2 = "topic.queue.test2";
String TOPIC_ROUTINGKEY_TEST1 = "*.orange.*";
String TOPIC_ROUTINGKEY_TEST2 = "*.orange.#";
}
写一个监听类,在本类中监听两个队列 topic.queue.test1、topic.queue.test2
@Component
@Slf4j
public class TopicReceiver {
@RabbitListener(queues = RabbitMqConstant.TOPIC_QUEUE_TEST1)
public void process1(Message message){
log.error("process1 receive msg: " + new String(message.getBody()));
}
@RabbitListener(queues = RabbitMqConstant.TOPIC_QUEUE_TEST2)
public void process2(Message message){
log.error("process2 receive msg: " + new String(message.getBody()));
}
}
2.2、rabbitmq-provider
引入RabbitMQ依赖:pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
配置文件:application.properties
server.port=9001
server.servlet.context-path=/provider
spring.application.name=rabbitmq-provider
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
RabbitMQ配置类:TopicRabbitConfig.java
@Configuration
public class TopicRabbitConfig {
@Bean
public TopicExchange topicExchangeTest(){
/**
* 创建一个主题型交换机
* 名字: topic.exchange.test
* durable(是否持久化): true
* autoDelete(是否自动删除,当没有生产者或者消费者使用此队列时,该队列会自动删除): false
*/
return new TopicExchange(RabbitMqConstant.TOPIC_EXCHANGE_TEST,true,false);
}
@Bean
public Queue topicQueueTest1(){
/**
* 创建一个队列
* 名字: topic.queue.test
* durable(是否持久化): true
* exclusive(默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable)
* autoDelete: false
*/
return new Queue(RabbitMqConstant.TOPIC_QUEUE_TEST1, true, false, false);
}
@Bean
public Queue topicQueueTest2(){
/**
* 创建一个队列
* 名字: topic.queue.test
* durable(是否持久化): true
* exclusive(默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable)
* autoDelete: false
*/
return new Queue(RabbitMqConstant.TOPIC_QUEUE_TEST2, true, false, false);
}
@Bean
Binding bindingTopic1(){
return BindingBuilder.bind(topicQueueTest1()).to(topicExchangeTest()).with(RabbitMqConstant.TOPIC_ROUTINGKEY_TEST1);
}
@Bean
Binding bindingTopic2(){
return BindingBuilder.bind(topicQueueTest2()).to(topicExchangeTest()).with(RabbitMqConstant.TOPIC_ROUTINGKEY_TEST2);
}
新建一个常量类:RabbitMqConstant.java
public interface RabbitMqConstant {
String TOPIC_EXCHANGE_TEST = "topic.exchange.test";
String TOPIC_QUEUE_TEST1 = "topic.queue.test1";
String TOPIC_QUEUE_TEST2 = "topic.queue.test2";
String TOPIC_ROUTINGKEY_TEST1 = "*.orange.*";
String TOPIC_ROUTINGKEY_TEST2 = "*.orange.#";
}
为了测试发送Message,这里写一个工具Controller:SendMessageController.java
1、首先来测试第一种情况:RoutingKey=“one.orange.two”
@RestController
public class SendMessageController {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("/sendMessageTopic")
public String sendMessageTopic(String msg){
for (int i = 0; i < 10; i++) {
rabbitTemplate.convertAndSend(RabbitMqConstant.TOPIC_EXCHANGE_TEST, "one.orange.two", msg + "#" + i);
}
return "success";
}
}
启动rabbitmq-provider
通过Postman来调用我们的Controller接口
调用成功后,去RabbitMQ管理页面查看,可以看到两个队列中都存在这10条消息。
注意:当rabbitmq-provider启动后,在RabbitMQ管理页面是看不到这两个队列,只有在扔消息时,RabbitMQ才会去创建这两个队列。
启动rabbitmq-consumer,会看到控制台日志打印:
2、测试第二种情况:RoutingKey=“orange.one”
停止启动rabbitmq-consumer(为了便于观察消息队列管理页面中的消息数量)
@RestController
public class SendMessageController {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("/sendMessageTopic")
public String sendMessageTopic(String msg){
for (int i = 0; i < 10; i++) {
rabbitTemplate.convertAndSend(RabbitMqConstant.TOPIC_EXCHANGE_TEST, "orange.two", msg + "#" + i);
}
return "success";
}
}
启动rabbitmq-provider
通过Postman来调用我们的Controller接口
调用成功后,去RabbitMQ管理页面查看,可以看到两个队列中都不存在这10条消息。
3、测试第三种情况:RoutingKey=“one.orange”
@RestController
public class SendMessageController {
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("/sendMessageTopic")
public String sendMessageTopic(String msg){
for (int i = 0; i < 10; i++) {
rabbitTemplate.convertAndSend(RabbitMqConstant.TOPIC_EXCHANGE_TEST, "one.orange", msg + "#" + i);
}
return "success";
}
}
启动rabbitmq-provider
通过Postman来调用我们的Controller接口
调用成功后,去RabbitMQ管理页面查看,可以看到只有在queue2队列中都存在这10条消息。