SpringBoot+RabbitMQ实现用接收器接受多个主题(Exchange-交换机初步使用)
本文适合学习RabbitMQ中的Exchange(交换机)的初学者。生产者生产消息通过交换机,根据绑定的规则分别分发给响应的消息队列(Queue),然后消费者再消费消息队列中的消息。注意:当我一运行项目后,如果创建了交换机(即通过RabbitMQ的管理界面可以看到交换机已经创建之后),那么之后如果项目的交换机即使做出了修改也不会生效,要想修改后的交换机生效,目前我的做法是去管理界面中删除它。 本文出现了三个生产者,一个交换机,三个消息队列(有一条不存在,没写),两个消费者
RabbitMQ系列文章如下:
(RabbitMQ 一【转载】)windows10环境下的RabbitMQ安装步骤
https://blog.csdn.net/myQNUMNINE/article/details/120447514
(RabbitMQ 二)Springboot项目中使用RabbitMQ的相关依赖
https://blog.csdn.net/myQNUMNINE/article/details/120460073
(RabbitMQ 三)SpringBoot+RabbitMQ实现发送和接收队列(可接受基本数据类型和对象)
https://blog.csdn.net/myQNUMNINE/article/details/120053242
(RabbitMQ 四)SpringBoot+RabbitMQ实现用接收器接受多个主题【一条消息通过交换机发往多个消息队列】
https://blog.csdn.net/myQNUMNINE/article/details/120065706
(RabbitMQ 五)SpringBoot+RabbitMQ一个接收者监听多个消息队列
https://blog.csdn.net/myQNUMNINE/article/details/120096394
(RabbitMQ 六)SpringBoot+RabbitMQ实现广播模式
https://blog.csdn.net/myQNUMNINE/article/details/120095529
(RabbitMQ 七【完结】)SpringBoot+RabbitMQ实现消息队列延迟功能(使用RabbitMQ延迟插件实现)
https://blog.csdn.net/myQNUMNINE/article/details/120335005
文章目录
一、配置topic(消息队列和交换机)
创建一个Rabbitmq的配置类(RabbitmqConfig),类上使用注解@Configuration;创建的消息队列(Queue)、交换机(TopicExchange)和绑定(Binding)的方法上需要使用@Bean注解。
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitmqConfigTopic {
/*创建两个队列*/
/**指定参数名为a,见下面@Qualifier("a"),不然在绑定的时候会报错误说不知道绑定哪个消息队列**/
@Bean(name = "a")
public Queue queueMessageA() {
return new Queue("topic.a");
}
@Bean(name = "b")
public Queue queueMessageB() {
return new Queue("topic.b");
}
/*创建一个交换机*/
@Bean
TopicExchange topicExchange() {
return new TopicExchange("topicExchange");
}
/*队列跟交换机绑定,传入参数为队列名(存在的)和交换机名(存在的)*/
@Bean
/*绑定消息队列topic.a*/
Binding bindingExchangeMessage(@Qualifier("a") Queue queue, TopicExchange topicExchange) {
/*队列-交换机-消息队列名(填我们创建的,或则全部,#相当于占位符,只要前面的相同就可以接收)*/
return BindingBuilder.bind(queue).to(topicExchange).with("topic.a");
}
@Bean
Binding bindingExchangeMessages(@Qualifier("b")Queue queue, TopicExchange topicExchange) {
/*topic.#:表示以topic.开头的全部由传入这个方法的队列处理,包括topic.a,即传入topic.2的消息会被处理两次*/
return BindingBuilder.bind(queue).to(topicExchange).with("topic.#");
}
}
具体来说一说绑定(Binding)的代码。.bind(queue)传入的消息队列,to(topicExchange).消息队列所要绑定的交换机,.with(“topic.a”)交换机所要发送消息的消息队列,这里是我们创建的topic.a,.with(“topic.#”)指的是消息队列名以topic.开头的一些列消息队列,包括已经绑定的topic.a。
需要值得注意的是,当我们启动了一次SpringBoot项目之后,那么就会创建交换机,当我们改变了交换机的绑定,在启动项目时,修改的绑定并不会生效。所以如果修改了交换机的绑定信息,请务必记得去RabbitMQ的管理界面删除原来的交换机,在启动项目哦。
@Qualifier(“a”) 说明使用的Bean类是name为a的。当有多个消息队列时,我们需要用@Bean(name=“xxx”)这种方式给Bean指定参数名。
二、编写接收者A和接收者B(消费者A和消费者B)
接收者A监听的主题是:topic.a;接收者B监听的主题是:topic.b
同样地,类上需要的注解有:@Component和@RabbitListener;方法上需要的注解是:@RabbitHandler
接收者A的代码如下:
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@RabbitListener(queues = "topic.a")
public class TopicReceiverA {
@RabbitHandler
public void process(String msg) {
System.out.println("接收者A接收了:"+msg);
}
}
接收者B的代码如下:
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@RabbitListener(queues = "topic.b")
public class TopicReceiverB {
@RabbitHandler
public void process(String msg) {
System.out.println("接收者B接收了:"+msg);
}
}
三、编写发送者(生产者)
编写发送者类,通过发送给不同的“topic”来测试效果。
类上需要的注解是:@Component;类的内部需要通过注解@Autowired自动注入MQ模板(AmqpTemplate)。
发送者类的代码如下:
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class TopicSender {
@Autowired
private AmqpTemplate amqpTemplate;
public void send() {
String string = "发给一个不存在的队列";
System.out.println("发送者:"+string);
this.amqpTemplate.convertAndSend("topicExchange","topic.1",string);
}
public void sendA() {
String string = "发给队列A";
System.out.println("发送者:"+string);
this.amqpTemplate.convertAndSend("topicExchange","topic.a",string);
}
public void sendB() {
String string = "发给队列B";
System.out.println("发送者:"+string);
this.amqpTemplate.convertAndSend("topicExchange","topic.b",string);
}
}
在这里解说一下这三个方法:其中这三个方法绑定的交换机都是同一个,名为:topicExchange;所要传输的数据都用string承接。不同的是,消息该给哪一个队列处理。
send()方法中处理的队列名为topic.1,但在以上的编码中我们没有创建名为topic.1的消息队列,但是我们在绑定叫换机的时候做了一个处理.with(“topic.#”),topic.1符合要求,所以send()方法所传的数据将会被接收者B消费,而接收者A不会。如果把.with(“topic.#”)变成.with(“topic.b”),那么send()所发送的消息将不会被接收者A和接收者B消费。
sendA()方法中处理的队列名为topic.a,经过交换机后,发现符合.with(“topic.a”)和.with(“topic.#”),所以这条消息将会被接收者A和接收者B消费,即会发送两次,分别给两个消费者消费。
sendB()方法中处理的队列名为topic.b,经过交换机后,发现符合.with(“topic.#”),所以这条消息会被接收者B消费,不会被接收者A消费。
四、编写测试
代码如下:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TopicSendController {
@Autowired
private TopicSender sender;
@GetMapping("/topicsend")
public void topic() throws Exception {
sender.send();
}
@GetMapping("/topicsend2")
public void topic1() throws Exception {
sender.sendA();
}
@GetMapping("/topicsend3")
public void topic2() throws Exception {
sender.sendB();
}
}
如果没有配置port的话,默认的端口则为8080;所以访问地址为:
send():http://localhost:8080/topicsend
sendA():http://localhost:8080/topicsend2
sendB():http://localhost:8080/topicsend3
总结
学到了这里RabbitMq的使用已经足够面对大部分情况了。