一、 Exchange(交换机)的作用
在RabbitMQ中,生产者发送消息不会直接将消息投递到队列中,而是先将消息投递到交换机中,在由交换机转发到具体的队列,
队列再将消息以推送或者拉取方式给消费者进行消费
创建消息 路由键 pull/push
生产者------------>交换机------------>队列------------>消费者
二、 Exchange(交换机)的类型
1)直连交换机:Direct Exchange
直连交换机是一种带路由功能的交换机,一个队列会和一个交换机绑定,除此之外再绑定一个routing_key,当消息被发送的时候,需要指定一个binding_key,这个消息被送达交换机的时候,就会被这个交换机送到指定的队列里面去。同样的一个binding_key也是支持应用到多个队列中的。
2)主题交换机:Topic Exchange
弥补直连交换机的缺点!
直连交换机的routing_key方案非常简单,如果我们希望一条消息发送给多个队列,那么这个交换机需要绑定上非常多的routing_key,
假设每个交换机上都绑定一堆的routing_key连接到各个队列上。那么消息的管理就会异常地困难。
所以RabbitMQ提供了一种主题交换机,发送到主题交换机上的消息需要携带指定规则的routing_key,
主题交换机会根据这个规则将数据发送到对应的(多个)队列上。
主题交换机的routing_key需要有一定的规则,交换机和队列的binding_key需要采用*.#.*…的格式,每个部分用.分开,其中
*表示一个单词
#表示任意数量(零个或多个)单词。
示例:
队列Q1绑定键为 *.TT.*
队列Q2绑定键为TT.#
如果一条消息携带的路由键为 A.TT.B,那么队列Q1将会收到
如果一条消息携带的路由键为TT.AA.BB,那么队列Q2将会收到
3)扇形交换机:Fanout Exchange
扇形交换机是最基本的交换机类型,它所能做的事情非常简单———广播消息。
扇形交换机会把能接收到的消息全部发送给绑定在自己身上的队列。因为广播不需要“思考”,
所以扇形交换机处理消息的速度也是所有的交换机类型里面最快的。
这个交换机没有路由键概念,就算你绑了路由键也是无视的。
4)首部交换机:Headers exchange
5)默认交换机
6)Dead Letter Exchange(死信交换机)
本文只讲前三种交换机
三、交换机的属性
除交换机类型外,在声明交换机时还可以附带许多其他的属性,其中最重要的几个分别是:
Name:交换机名称
Durability:是否持久化。如果持久性,则RabbitMQ重启后,交换机还存在
Auto-delete:当所有与之绑定的消息队列都完成了对此交换机的使用后,删掉它
Arguments:扩展参数
**四、案例:交换机的使用 **
打开linux,开启docker,开启容器,容器的创建请参照
https://blog.csdn.net/qq_43469718/article/details/103671005
输入界面版RabbitMQ
创建springcloud项目
rabbitmq02 #主模块
rabbitmq-provider #生产者
rabbitmq-consumer #消费者
给父模块添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.zking</groupId>
<artifactId>rabbitmq02</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>rabbitmq02</name>
<packaging>pom</packaging>
<description>Demo project for Spring Boot</description>
<modules>
<module>rabbitmq-provider</module>
<module>rabbitmq-consumer</module>
</modules>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
子模块pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.zking</groupId>
<artifactId>rabbitmq02</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>rabbitmq-provider</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>rabbitmq-provider</name>
<packaging>jar</packaging>
<description>Demo project for Spring Boot</description>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.zking</groupId>
<artifactId>rabbitmq02</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>rabbitmq-consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>rabbitmq-consumer</name>
<packaging>jar</packaging>
<description>Demo project for Spring Boot</description>
</project>
配置yml
rabbitmq-provider
server:
port: 8081
servlet:
context-path: /rabbitmq-provider
spring:
rabbitmq:
virtual-host: my_vhost
host: 192.168.208.130
port: 5672
username: admin
password: admin
rabbitmq-consumer
server:
port: 8082
servlet:
context-path: /rabbitmq-consumer
spring:
rabbitmq:
virtual-host: my_vhost
host: 192.168.208.130
port: 5672
username: admin
password: admin
1.演示直连交换机:Direct Exchange
在生产者rabbitmq-provider
新建包rabbitmq
,这个包专门用来放配置类
新建类RabbitDirect
配置类
package com.zking.rabbitmqprovider.rabbitmq;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/*
* 功能描述:
* 〈〉用于绑定队列交换机路由键的关系
* 示例:直接交换机(Driect Exchange)
* @Param:
* @Return:
* @Author: 骄傲的骨傲天
* @Date: 2019/12/23 22:27
*/
@Configuration
public class RabbitDirect {
/*
* 功能描述:
* 〈〉1.定义一队列
* @Param:
* @Return:
* @Author: 骄傲的骨傲天
* @Date: 2019/12/23 22:34
*/
@Bean
public Queue queue(){
return new Queue("queue-1",true);
}
/*
* 功能描述:
* 〈〉2.定义直接交换机
* @Param:
* @Return:
* @Author: 骄傲的骨傲天
* @Date: 2019/12/23 22:34
*/
@Bean
public DirectExchange directExchange(){
return new DirectExchange("direct-exchange");
}
/*
* 功能描述:
* 〈〉3.将队列与直连交换机进行绑定,并以路由键来串联
* @Param:
* @Return:
* @Author: 骄傲的骨傲天
* @Date: 2019/12/23 22:36
*/
@Bean
public Binding directBinding(){
return BindingBuilder.bind(queue()).
to(directExchange()).with("direct-routing-key");
}
}
新建controller
包与rabbitmq
同级目录
新建类SendController
用户来向交换机发送消息根据路由键传到消息队列中
此类头上有个@RestController注解
@Autowired
private RabbitTemplate rabbitTemplate;
@RequestMapping("/senderByDricet")
public Map<String,Object> senderByDricet(){
Map<String,Object> map=new HashMap<String,Object>();
Map<String, Object> data = this.createData();
log.info("生产者发送消息,queue={},directExchange={},routingkey={}",
"queue-1","direct-exchange","direct-routing-key");
log.info("msg={}",data);
//第一个参数:交换机的名称
//第二个参数:路由键
//第三个参数:数据
rabbitTemplate.convertAndSend("direct-exchange","direct-routing-key",data);
map.put("code",1);
map.put("msg","消息发送成功");
return map;
}
public Map<String,Object> createData(){
Map<String,Object> data=new HashMap<String,Object>();
String ceateDate= LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
String message="hello,rabbitmq!!";
data.put("createDate",ceateDate);
data.put("massage",message);
return data;
}
运行
postman测试一下
再看下RabbitMQ web版有没有此数据列表
可以看到名为queue-1的消息队列中是有一条消息的
往消费者rabbitmq-consumer
写入代码,消费此消息
创建rabbitmq
包
创建RabbitDirectReceiver
类
开始消费
package com.zking.rabbitmqconsumer.rabbitmq;
import lombok.extern.slf4j.Slf4j;
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 = {"queue-1"})
@Slf4j
public class RabbitDirectReceiver {
@RabbitHandler
public void handlerMessage(Map<String,Object> map){
log.info("消费者接收消息。。。");
log.info("msg={}",map);
}
}
看下消息已经被消费了
关闭消费者rabbitmq-consumer
,不让进行消费,因为演示主题交换机需要看到效果
2.演示主题交换机:Topic Exchange
同理我们先来制造消息
在生产者rabbitmq-provider
中的rabbitmq
包下创建主题交换机配置类RabbitTopicConfig
package com.zking.rabbitmqprovider.rabbitmq;
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;
/*
* 功能描述:
* 〈〉主题交换器
* @Param:
* @Return:
* @Author: 骄傲的骨傲天
* @Date: 2019/12/23 23:33
*/
@Configuration
public class RabbitTopicConfig {
@Bean
public Queue queueX(){
return new Queue("queue-x",true);
}
@Bean
public Queue queueY(){
return new Queue("queue-y",true);
}
@Bean
public Queue queueZ(){
return new Queue("queue-z",true);
}
@Bean
public TopicExchange topicExchange(){
return new TopicExchange("topic-exchange");
}
@Bean
public Binding bindingX(){
return BindingBuilder.bind(queueX()).to(topicExchange())
.with("topic.person.xxx");
}
@Bean
public Binding bindingY(){
return BindingBuilder.bind(queueY()).to(topicExchange())
.with("topic.person.yyy");
}
@Bean
public Binding bindingZ(){
return BindingBuilder.bind(queueZ()).to(topicExchange())
.with("topic.person.*");
}
}
可以看到我创建了三个消息队列:queue-x
queue-y
queue-z
注意看bindingZ
方法的路由键规则topic.person.*
主题交换机的routing_key需要有一定的规则,交换机和队列的binding_key
需要采用*.#.*.....
的格式,每个部分用.
分开,其中
*
表示一个单词
#
表示任意数量(零个或多个)单词。
然后再controller
包下的SendController
类中添加senderByTopic
方法(演示直连交换机所创建的)
@RequestMapping("/senderByTopic")
public Map<String,Object> senderByTopic(String routingKey){
Map<String,Object> map=new HashMap<String,Object>();
Map<String, Object> data = this.createData();
log.info("生产者发送消息,directExchange={},routingkey={}",
"topic-exchange",routingKey);
log.info("msg={}",data);
//第一个参数:交换机的名称
//第二个参数:路由键
//第三个参数:数据
rabbitTemplate.convertAndSend("topic-exchange",
routingKey,data);
map.put("code",1);
map.put("msg","消息发送成功");
return map;
}
此方法中有个routingKey,这就是路由键,是活的,从前端传过的
重启rabbitmq-provider
,使用postman测试一下
结果:
可以发现我的路由键参数为:topic.person.yyy
同时匹配queue-y
和queue-z
的规则,所以两个消息队列中都有消息
往消费者rabbitmq-consumer
写入代码,消费此两条消息
在rabbitmq
包下创建三个类:RabbitTopicReceiverX
RabbitTopicReceiverY
RabbitTopicReceiverZ
package com.zking.rabbitmqconsumer.rabbitmq;
import lombok.extern.slf4j.Slf4j;
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 = {"queue-x"})
@Slf4j
public class RabbitTopicReceiverX {
@RabbitHandler
public void handlerMessage(Map<String,Object> map){
log.info("消费者接收消息。。。");
log.info("RabbitTopicReceiverX.handlermessage={}",map);
}
}
package com.zking.rabbitmqconsumer.rabbitmq;
import lombok.extern.slf4j.Slf4j;
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 = {"queue-y"})
@Slf4j
public class RabbitTopicReceiverY {
@RabbitHandler
public void handlerMessage(Map<String,Object> map){
log.info("消费者接收消息。。。");
log.info("RabbitTopicReceiverY.handlermessage={}",map);
}
}
package com.zking.rabbitmqconsumer.rabbitmq;
import lombok.extern.slf4j.Slf4j;
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 = {"queue-z"})
@Slf4j
public class RabbitTopicReceiverZ {
@RabbitHandler
public void handlerMessage(Map<String,Object> map){
log.info("消费者接收消息。。。");
log.info("RabbitTopicReceiverZ.handlermessage={}",map);
}
}
3.演示扇形交换机
扇形交换机是与路由键无关的
首先在生产者rabbitmq-provider
下的rabbitmq
中创建扇形交换机的配置类RabbitFanoutConfig
package com.zking.rabbitmqprovider.rabbitmq;
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;
import javax.validation.executable.ValidateOnExecution;
/*
* 功能描述:
* 〈〉扇形交换机,用于广播消息,于路由键无关
* @Param:
* @Return:
* @Author: 骄傲的骨傲天
* @Date: 2019/12/23 23:32
*/
@Configuration
public class RabbitFanoutConfig {
@Bean
public Queue queueA(){
return new Queue("queue-a",true);
}
@Bean
public Queue queueB(){
return new Queue("queue-b",true);
}
@Bean
public Queue queueC(){
return new Queue("queue-c",true);
}
@Bean
public FanoutExchange fanoutExchange(){
return new FanoutExchange("fanout-exchange");
}
@Bean
public Binding bindingA(){
return BindingBuilder.bind(queueA()).to(fanoutExchange());
}
@Bean
public Binding bindingB(){
return BindingBuilder.bind(queueB()).to(fanoutExchange());
}
@Bean
public Binding bindingC(){
//可以看到这里不需要with
return BindingBuilder.bind(queueC()).to(fanoutExchange());
}
}
在controller
包中的SendController
中添加方法
@RequestMapping("/senderByFanout")
public Map<String,Object> senderByFanout(){
Map<String,Object> map=new HashMap<String,Object>();
Map<String, Object> data = this.createData();
log.info("生产者发送消息,fanoutExchange={}",
"fanout-exchange");
log.info("msg={}",data);
//第一个参数:交换机的名称
//第二个参数:路由键
//第三个参数:数据
rabbitTemplate.convertAndSend("fanout-exchange",
null,data);
map.put("code",1);
map.put("msg","消息发送成功");
return map;
}
可以看到rabbitTemplate.convertAndSend("fanout-exchange", null,data);
路由键是null
因此,此方法发送消息所有与fanout-exchange
交换机关联的消息队列都会被发送消息
运行生产者rabbitmq-provider
postman测试一下
三个消息队列都有消息
往消费者rabbitmq-consumer
写入代码,消费消息
在rabbitmq
包下写入三个类:RabbitFanoutReceiverA
RabbitFanoutReceiverB
RabbitFanoutReceiverC
package com.zking.rabbitmqconsumer.rabbitmq;
import lombok.extern.slf4j.Slf4j;
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 = {"queue-a"})
@Slf4j
public class RabbitFanoutReceiverA {
@RabbitHandler
public void handlerMessage(Map<String,Object> map){
log.info("消费者接收消息。。。");
log.info("RabbitTopicReceiverA.handlermessage={}",map);
}
}
package com.zking.rabbitmqconsumer.rabbitmq;
import lombok.extern.slf4j.Slf4j;
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 = {"queue-b"})
@Slf4j
public class RabbitFanoutReceiverB {
@RabbitHandler
public void handlerMessage(Map<String,Object> map){
log.info("消费者接收消息。。。");
log.info("RabbitTopicReceiverB.handlermessage={}",map);
}
}
package com.zking.rabbitmqconsumer.rabbitmq;
import lombok.extern.slf4j.Slf4j;
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 = {"queue-c"})
@Slf4j
public class RabbitFanoutReceiverC {
@RabbitHandler
public void handlerMessage(Map<String,Object> map){
log.info("消费者接收消息。。。");
log.info("RabbitTopicReceiverC.handlermessage={}",map);
}
}