在上节中,说了RabbitMQ安装与其流程图以及直连交换机的用法与逻辑。本节,主要学习扇形交换机与主题交换机的用法。
初学RabbitMQ(一),使用SpringBoot进行简单的整合,初识直连交换机。
扇形交换机 amq.fanout
可以会用多个队列绑定一个扇形交换机,消费者消费其中的数据时,会拉取扇形交换机中绑定的所有队列。
理解了扇形交换机,那么就编写一下生产者mqproducer的代码,pom的依赖文件如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
对应的配置文件为:
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FanoutConfig {
@Bean
Queue firstQueue(){
return new Queue("firstFanoutQueue",true);
}
@Bean
Queue secondQueue(){
return new Queue("secondFanoutQueue",true);
}
@Bean
Queue thirdQueue(){
return new Queue("thirdFanoutQueue",true);
}
@Bean
Binding bindingFirstExchange(){
return BindingBuilder.bind(firstQueue()).to(new FanoutExchange("amq.fanout"));
}
@Bean
Binding bindingSecondExchange(){
return BindingBuilder.bind(secondQueue()).to(new FanoutExchange("amq.fanout"));
}
@Bean
Binding bindingThirdExchange(){
return BindingBuilder.bind(thirdQueue()).to(new FanoutExchange("amq.fanout"));
}
}
简单的发送消息的方法为(需要根据场景进行消息发送):
@RequestMapping("/sendtofanout")
public void sendToFanout(){
try{
String uuid = String.valueOf(UUID.randomUUID());
String message = "This is a first message to Fanout!";
String date = new SimpleDateFormat("yyyy-MM-dd HH:mm").format(new Date());
Map<String,Object> m = new HashMap<>();
m.put("messageId", uuid);
m.put("createTime",date);
m.put("message",message);
rabbitTemplate.convertAndSend("amq.fanout",null,m);
}catch(Exception e){
e.printStackTrace();
}
}
启动项目后,访问http://localhost:15672/会发现,定义的三个队列都绑定到了amq.fanout交换机上,调用发送接口后就会发现,每个队列都有一条消息正在等待消费。
此刻开始编写消费者的代码,其中配置文件如下(消费者可以不写,因为消费者是获取消息的主体,使用后面的注解监听器就好了,若配置了配置文件,这消费者也可以当成是发送消息的主体。
)
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CustomerFanoutConfig {
@Bean
Queue firstQueue(){
return new Queue("firstFanoutQueue",true);
}
@Bean
Queue secondQueue(){
return new Queue("secondFanoutQueue",true);
}
@Bean
Queue thirdQueue(){
return new Queue("thirdFanoutQueue",true);
}
@Bean
Binding bindingFirstExchange(){
return BindingBuilder.bind(firstQueue()).to(new FanoutExchange("amq.fanout"));
}
@Bean
Binding bindingSecondExchange(){
return BindingBuilder.bind(secondQueue()).to(new FanoutExchange("amq.fanout"));
}
@Bean
Binding bindingThirdExchange(){
return BindingBuilder.bind(thirdQueue()).to(new FanoutExchange("amq.fanout"));
}
}
接下来编写三个消息监听器,用于监听三个队列的数据,便于学习:
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.util.Map;
@Component
@RabbitListener(queues = {"firstFanoutQueue"})
public class CustomerFanoutFirstListener {
@RabbitHandler
public void process(Map m){
System.out.println("CustomerFanoutFirstListener接受到的消息为:"+m.toString());
}
}
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.util.Map;
@Component
@RabbitListener(queues = {"secondFanoutQueue"})
public class CustomerFanoutSecondListener {
@RabbitHandler
public void process(Map m){
System.out.println("CustomerFanoutSecondListener接受到的消息为:"+m.toString());
}
}
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.util.Map;
@Component
@RabbitListener(queues = {"thirdFanoutQueue"})
public class CustomerFanoutThirdListener {
@RabbitHandler
public void process(Map m){
System.out.println("CustomerFanoutThirdListener接受到的消息为:"+m.toString());
}
}
此时启动消费者项目后,每个监听器都会监听到一个消息
此时若是再访问一下发送消息的接口,消费者也会实时的进行消费:
主题交换机 amq.topic
主题交换机中,绑定的路由值可以使用匹配符进行模糊匹配。
#号表示的是匹配0个或者多个
*号表示的是匹配1个或者多个
举个例子:
若有路由值设定为:topic.man、topic.#
那么若有消费者携带的路由键为,那么topic.woman则可以消费成功。
那么开始编写生产者mqproducer代码,其配置类如下:
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class TopicConfig {
@Bean
Queue topicQueue(){
return new Queue("topic.man",true);
}
@Bean
Queue topicQueue1(){
return new Queue("topic.woman",true);
}
@Bean
Binding bindingTopicExchange2(){
return BindingBuilder.bind(topicQueue()).to(new TopicExchange("amq.topic")).with("topic.man");
}
@Bean
Binding bindingTopicExchange1(){
return BindingBuilder.bind(topicQueue1()).to(new TopicExchange("amq.topic")).with("topic.# ");
}
}
接下来写一下简单的发送消息的接口:
@RequestMapping("/sendtotopic1")
public void sendToTopicMan(){
try{
String uuid = String.valueOf(UUID.randomUUID());
String message = "This is a first message to topic : Man!";
String date = new SimpleDateFormat("yyyy-MM-dd HH:mm").format(new Date());
Map<String,Object> m = new HashMap<>();
m.put("messageId", uuid);
m.put("createTime",date);
m.put("message",message);
rabbitTemplate.convertAndSend("amq.topic","topic.man",m);
}catch(Exception e){
e.printStackTrace();
}
}
@RequestMapping("/sendtotopic2")
public void sendToTopicWoman(){
try{
String uuid = String.valueOf(UUID.randomUUID());
String message = "This is a first message to topic : Woman!";
String date = new SimpleDateFormat("yyyy-MM-dd HH:mm").format(new Date());
Map<String,Object> m = new HashMap<>();
m.put("messageId", uuid);
m.put("createTime",date);
m.put("message",message);
rabbitTemplate.convertAndSend("amq.topic","topic.asdasd",m);
}catch(Exception e){
e.printStackTrace();
}
}
现在开始编写一下消费者的代码,配置文件如下(可以不用写):
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CustomerTopicConfig {
@Bean
Queue topicQueue(){
return new Queue("topic.man",true);
}
@Bean
Queue topicQueue1(){
return new Queue("topic.woman",true);
}
@Bean
Binding bindingTopicExchange2(){
return BindingBuilder.bind(topicQueue()).to(new TopicExchange("amq.topic")).with("topic.man");
}
@Bean
Binding bindingTopicExchange1(){
return BindingBuilder.bind(topicQueue1()).to(new TopicExchange("amq.topic")).with("topic.# ");
}
}
监听消息的代码如下,需要建两个监听类,一个监听topic.man一个监听topic.woman两个队列:
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.util.Map;
@Component
@RabbitListener(queues = {"topic.man"})
public class CustomerTopicFirstListener {
@RabbitHandler
public void process(Map m){
System.out.println("CustomerTopicFirstListener接受到的消息为:"+m.toString());
}
}
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.util.Map;
@Component
@RabbitListener(queues = {"topic.woman"})
public class CustomerTopicSecondListener {
@RabbitHandler
public void process(Map m){
System.out.println("CustomerTopicSecondListener接受到的消息为:"+m.toString());
}
}
此刻启动生产者项目和消费者项目,访问RabbitMQ服务端就可以发现了,队列已经与主题交换机绑定好了,此时访问sendtotopic1接口,可以发现两个队列都接受到了消息,说明路由键匹配上了。
这时候,访问一下sendtotopic2接口,可以发现只有topic.woman的队列匹配上了。
在sendtotopic2接口代码中,我携带的路由键是topic.asdasd,这就是匹配符的作用,句点之后无论写什么,都能匹配到路由值topic.#绑定的队列上。
主题交换机是很强大的,为什么呢?
这是因为主题交换机可以做为直连交换机或者扇形交换机的使用。
若绑定的路由键直接设置为#号,则默认任何消息携带的任何路由键都能匹配,可以作为扇形交换机使用。
若绑定的路由键不包含#号和*号,就可以作为直连交换机使用。
本节记录了主题交换机与扇形交换机的用法。