🎉🎉欢迎来到我的CSDN主页!🎉🎉
🏅我是君易--鑨,一个在CSDN分享笔记的博主。📚📚
🌟推荐给大家我的博客专栏《RabbitMQ系列之交换机的使用》。🎯🎯
🎁如果感觉还不错的话请给我关注加三连吧!🎁🎁
目录
前言
在上一期的RabbitMQ系列的博客分享中我们对其MQ的概念及使用的场景有了一个初步的认识以及了解,并介绍了几种常见的实现的方式,有RabbitMQ、ActiveMQ、 Amazon Simple Queue Service (SQS)等等。本期博客基于上期博客的代码基础进行讲解RabbitMQ中的交换机的使用。
一、交换机的简介
1. 什么是交换机
在RabbitMQ中,交换机是一个核心概念,它决定了消息在RabbitMQ中的路由策略,也即决定了消息最终到达哪个队列。交换机是用来发送消息的AMQP实体,它拿到一个消息之后将它路由给一个或多个队列。交换机根据其类型和绑定的规则来决定使用哪种路由算法。
2. 交换机的作用
交换机在RabbitMQ中扮演着重要的角色,它的作用是:生产者发送消息不会像传统方式直接将消息投递到队列中,而是先将消息投递到交换机中,再由交换机转发到具体的队列,队列再将消息以推送或者拉取方式给消费者进行消费。这样可以根据具体的路由策略将消息分发到不同的队列中。(交换机的功能可以通俗地理解为“邮局”,负责传递信息,它通过对信息进行重新生成,并经过内部处理后转发至指定端口,具备自动寻址能力和交换作用。)
交换机具有以下作用:
- 连接设备:交换机负责连接网络设备和终端设备,如交换机、路由器、防火墙、无线AP、计算机、服务器、摄像头、网络打印机等,实现所有设备之间的通信。
- 构建局域网络:交换机可以构建局域网络,通过与其他网络设备的互联,如无线接入点、路由器、网络防火墙等,实现局域网与局域网的互联以及局域网与Internet的互联。
- 扩展网络范围:交换机可以最大限度地扩展网络的范围,提供不同的网络技术来改善传输的距离,特别是对于传输距离非常远的网络来说非常有用。
- 实现端口之间的密切联系:以太网交换机可以让端口之间直接产生密切联系,特别是对于网络需求特别大的企业来说,可以同时提供多个端口,满足多数人的网络需要。
3. 交换机的应用场景
这里就以RabbitMQ的交换机为例说一说交换机的一些应用场景有哪些,如下所示。
应用场景 | 说明 |
异步处理 | 如果改为了MQ方式,就可以进行异步处理,且短信涉及到的网络情况时间还长,与第三方交互也会有些情况,改用MQ异步处理,就可以支持更多的并发,也可以根据业务的量进行随时扩容。 |
发布订阅模式 | 发布订阅是作为生产者产生的一个消息,他的消费者都可以收到此条消息。在这种模式下,交换机可以将收到的消息发给多套副本队列,消费者如果订阅了这个队列的话,就可以收到生产者的消息了。这种模式中,所有消费者拿到的消息是完全相同的。 |
4. 图解说明
在RabbitMQ中,生产者发送消息不会直接价格消息投递给队列中,而是先将消息投递给交换机中,在由交换机转发到具体的队列,队列再将消息以推送或者拉去的方式给消费者进行消费。
其中包含两个关键组件:
路由键:生产者将消息发送给交换机的时候,会指定RoutingKey指定路由的规则。
绑定键:通过绑定键将交换机与队列关联起来,这样RabbitMQ就知道如何正确地将消息路由到队列。
5.交换机属性
属性 | 说明 |
Type | 交换机类型(direct、topic、fanout、headers) |
Durability | 是否需要持久化,如果持久化,则RabbitMQ重启后,交换机还存在。 |
Auto Delete | 当最后一个绑定到Exchange上的队列删除后,自动删除该Exchange |
Internal | 当前Exchange是否用于RabbitMQ内部使用,默认false |
Argument | 扩展参数,用于扩展AMQP协议定制化使用 |
二、 交换机类型讲解
1. 直连交换机(Direct exchange)
1.1 基本概述
直连交换机的路由算法非常简单:将消息推送到binding key与该消息的routing key相同的队列。
图解
像上图所示,我们直连交换机的绑定了两个队列。第一个队列绑定了orange,第二个队列绑定了两个绑定键:black和green。在这种场景下,一个消息在发布消息时指定了orange将只能被队列Q1接收, 在发布消息时指定了black和green将只能被队列Q2接收, 其他消息将丢失。
同一个绑定键可以绑定到不同的队列上去,可以增加一个交换机X与队列Q2的绑定键,在这种情况下,直连交换机和广播交换机有着相同的行为,将消息推送到所有的匹配队列上。(如下图)
1.2 特点
直连交换机是交换机的一种类型,它的特点主要包括以下几点
特点 | 说明 |
公平调度 | 当接收端订阅者有多个时,直连交换机能够轮询公平地分发消息给每个订阅者。 |
消息的发后既忘特性 | 直连交换机中,接收者不知道消息的来源,如果想要指定消息的发送者,需要在发送内容中包含相关信息。 |
消息确认 | 直连交换机要求接收者在接收到消息后使用特定的方法(如channel.basicAck())进行手动确认。如果接收者未进行确认,消息的状态将从“Ready”变为“Unacked”,直到确认或断开连接后才会改变状态。 |
消息拒绝 | 在未确认之前,接收者可以选择拒绝接收消息,例如通过断开与交换机的连接或使用特定的方法拒绝消息。 |
内部交换矩阵和高带宽 | 直连交换机具有高带宽的内部交换矩阵和背部总线,可以迅速而直接地将数据包传输到目标节点,避免了不必要的网络资源浪费,提高了效率。 |
独立网段和固定带宽 | 在同一个时间段内,直连交换机可以将数据传输到多个节点之间,每个节点都可以作为独立网段而独自享有固定的部分带宽,避免了与其他设备的竞争。 |
工作于数据链路层 | 直连交换机主要工作在OSI模型的物理层和数据链路层,不依赖于三层地址和路由信息。 |
绑定多个Routing Key | 如果希望一条消息发送到多个队列,直连交换机需要绑定大量的Routing Key。 |
1.3 缺点
直连交换机的缺点主要包括以下几点:
- 安全性差:使用一个交换机会使不同网段的设备处于同一广播域内,容易受到攻击和干扰。
- 扩展性有限:直连交换机的网络规模和节点数量有限,无法满足大规模网络的需求。
- 维护和管理困难:由于直连交换机需要手动配置和管理,对于大规模的网络来说,管理和维护成本较高。
- 带宽和资源利用率低:直连交换机无法实现动态带宽分配和负载均衡,容易造成资源浪费和带宽瓶颈。
- 无法支持大规模并发连接:直连交换机的并发连接数有限,无法满足大规模并发的需求。
2. 主题交换机(Topic exchange)
2.1 基本概述
主题交换机(Topic Exchange)是一种消息队列的交换机类型,它通过对消息的路由键和队列到交换机的绑定模式之间的匹配,将消息路由给一个或多个队列。主题交换机经常用来实现各种发布/订阅模式及其变种,通常用来实现消息的多播路由。
发送消息绑定的routing key | 接受消息的队列 queue |
quick.orange.rabbit | Q1,Q2 |
lazy.orange.elephant | Q1,Q2 |
quick.orange.fox | Q1 |
lazy.brown.fox | Q2 |
lazy.pink.rabbit | Q2 |
quick.brown.fox | 没有队列接收 |
2.2 特点
主题交换机的特点是:
特点 | 说明 |
模糊匹配 | 主题交换机支持模糊匹配,可以使用星号(*)和井号(#)作为通配符进行匹配。其中,* 可以代替一个单词,# 可以代替任意个单词。 |
队列绑定 | 主题交换机通过队列绑定来实现消息的路由,可以将一个或多个队列绑定到交换机上,并根据匹配规则将消息路由到相应的队列中。 |
多播路由 | 主题交换机可以实现消息的多播路由,即将一条消息发送给多个队列,满足发布/订阅模式的需求。 |
2.3 应用场景
主题交换机的应用场景包括:
- 发布/订阅模式:主题交换机适用于实现发布/订阅模式及其变种,例如在新闻网站、博客平台等场景中,发布者发布一条消息后,订阅者可以接收到该消息。
- 消息分发:主题交换机可以将一条消息同时发送给多个队列,实现消息的分发和多播路由,适用于需要将消息同时发送给多个消费者的场景,例如日志收集、监控系统等。
- 异步处理:主题交换机可以将消息异步地发送给消费者处理,提高系统的并发性能和吞吐量,适用于需要进行大量异步处理的场景,例如在线游戏、社交网络等。
需要注意的是,主题交换机在不同的消息队列系统中可能具有不同的实现方式和特点。在实际应用中,需要根据具体的需求和场景选择适合的消息队列系统和主题交换机类型。
3. 扇形交换机(Fanout exchange)
3.1 基本概述
扇形交换机是最基本的交换机类型,它的作用是广播消息。扇形交换机会把能接收到的消息全部发送给绑定在自己身上的队列。因为广播不需要思考,所以扇形交换机处理消息的速度也是所有交换机类型里面最快的。
3.2 特点
扇形交换机具有以下特点:
- 广播消息:扇形交换机能够将接收到的消息广播给所有绑定在其上的队列。
- 速度快:由于广播不需要“思考”,所以扇形交换机处理消息的速度非常快,是所有交换机类型中速度最快的。
3.3 应用场景
扇形交换机通常用于以下应用场景:
- 大规模多用户在线(MMO)游戏:这种场景中,可能需要对排行榜更新等全局事件进行大规模广播,使用扇形交换机可以将消息广播给所有消费者,确保所有用户都能收到相同的消息。
- 体育新闻网站:体育新闻网站需要近乎实时地将比分更新分发给移动客户端。使用扇形交换机可以将比分更新广播给所有订阅的移动客户端,确保所有用户都能接收到最新的比分信息。
- 分发系统:在分发系统,如群聊中,扇形交换机被用来分发消息给参与群聊的用户。
4. 首部交换机(Headers exchange)
4.1 基本概述
首部交换机是一种特殊的交换机类型,它不依赖于路由键的匹配规则来路由消息,而是根据发送的消息内容中的headers属性进行匹配。在绑定队列和交换器时,可以制定一组键值对。当发送消息到交换器时,RabbitMQ会获取到该消息的headers(也是一个键值对的形式),并对比其中的键值对是否完全匹配队列和交换器绑定时指定的键值对。如果完全匹配,则消息会路由到该队列,否则不会路由到该队列。
4.2 特点
首部交换机的特点主要包括以下几点:
- 基于headers属性匹配:首部交换机根据发送的消息内容中的headers属性进行匹配,而不是依赖于路由键的匹配规则。
- 性能较差:由于需要对比消息的headers属性与队列和交换器绑定时指定的键值对,因此首部交换机的性能会比较差,且不实用。
- 无法看到实际应用:在实际应用中,很少看到首部交换机的存在。
4.3 应用场景
首部交换机的应用场景较少,主要是用于某些特殊场景,例如企业网络、商务大厦网络、酒店宽带网络等。在这些场景中,可能需要实现数据的传输和交换,并且需要支持带宽控制、流量管理、VLAN等功能。但是,由于首部交换机的性能较差,且不实用,因此在实际应用中并不常见。
5. 默认交换机
5.1 基本概述
默认交换机(default exchange)实际上是一个由消息代理预先声明好的没有名字(名字为空字符串)的直连交换机(direct exchange)。它有一个特殊的属性使得它对于简单应用特别有用处,那就是每个新建队列(queue)都会自动绑定到默认交换机上,绑定的路由键(routing key)名称与队列名称相同。
举个例子,当你声明了一个名为"search-indexing-online"的队列,AMQP代理会自动将其绑定到默认交换机上,绑定(binding)的路由键名称也是为"search-indexing-online"。换句话说,默认交换机看起来貌似能够直接将消息投递给队列,尽管技术上并没有做相关的操作。
6. 死信交换机
6.1 基本概述
死信交换机是一种特殊的交换机,用于接收和路由成为死信的消息。当消息成为死信后,会被发送到死信交换机,并通过该交换机的路由规则,将消息路由到指定的死信队列中进行处理。
当队列中的消息满足以下情况之一时,可以成为死信:
- 消费者使用basic.reject或basic.nack声明消费失败,并且消息的requeue参数设置成了false。
- 消息是一个过期消息,超时无人消费。
- 要投递的队列消息满了,最新进来的消息变成死信。
如果这个包含死信的队列配置了dead-letter-exchange属性,指定了一个交换机,那么队列中的死信就会投递到交换机中,而这个交换机就被称为死信交换机(Dead Letter Exchange)。
死信交换机可以用于收集所有消费者处理失败的消息(死信),交由人工处理,进一步提高消息队列的可靠性
6.2 图解说明
6.3 案例图解
三 、实战演示
我们先创建两个队列用于我们实现对应的效果演示。
@Bean
// 声明第一个队列 。
public Queue queue01(){
return new Queue("queue01");
}
@Bean
// 声明第二个队列
public Queue queue02(){
return new Queue("queue02");
}
1. 直连交换机
生产者
1.1 创建直连交换机
// 创建直连交换机
@Bean
// 创建交换机
public DirectExchange directExchange(){
return new DirectExchange("exchange01");
}
1.2 绑定队列及交换机的关系
@Bean
// 绑定队列和交换机
public Binding binding01(){
return BindingBuilder
.bind(queue01())
.to(directExchange())
.with("yx");
}
@Bean
// 绑定队列和交换机
public Binding binding02(){
return BindingBuilder
.bind(queue02())
.to(directExchange())
.with("zxy");
}
1.3 编写对应的请求方法
我们在controller类中编写对应的请求方法
@RequestMapping("/send3")
public String send3() {
// 向交换机发送消息
amqpTemplate.convertAndSend("directExchange","yx","木易");
return "木易";
}
@RequestMapping("/send4")
public String send4() {
// 向交换机发送消息
amqpTemplate.convertAndSend("directExchange","zxy","馨月");
return "木易";
}
消费者
我们在消费者这边编写独有的代码进行接受生成者发送的请求信息。
ReceiverQ1.java
package com.yx.consumer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@SuppressWarnings("all")
@Slf4j
@RabbitListener(queues = "queue01") //接收的队列
public class ReceiverQ1 {
@RabbitHandler
public void process(String msg) {
log.warn("queue01接收到:" + msg);
}
}
ReceiverQ2.java
package com.yx.consumer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@SuppressWarnings("all")
@Slf4j
@RabbitListener(queues = "queue02") //接收的队列
public class ReceiverQ2 {
@RabbitHandler
public void process(String msg) {
log.warn("queue02接收到:" + msg);
}
}
测试效果
我们在测试之前要先说名,我们应该先启动我们的生产者并且访问我们的请求方法使其RabbitMQ管理器生成对应的队列或者前往官网管理器手动添加对应的队列,否则我们启动消费者或者启动消费者,消费者的服务端控制台回报错。
因此我们先启动生成者访问对应的接口方法生成对应的队列。
我们启动消费者服务进行接受信息
2. 主题交换机
// 规则 :
// *.*.yx->Q1
// *.*.zxy->Q2
// mq.# --Q1,Q2
生产者
2.1 创建主题交换机
// 创建主题交换机
@Bean
public TopicExchange topicExchange(){
return new TopicExchange("topicExchange");
}
2.2 绑定队列及交换机的关系
// 绑定主题交换机 queue01
@Bean
public Binding binding03(){
return BindingBuilder
.bind(queue01())
.to(topicExchange())
.with("*.*.yx");
}
// 绑定主题交换机 queue02
@Bean
public Binding binding04(){
return BindingBuilder
.bind(queue01())
.to(topicExchange())
.with("*.*.zxy");
}
// 绑定主题交换机 queue01和queue02
@Bean
public Binding binding05(){
return BindingBuilder
.bind(queue01())
.to(topicExchange())
.with("mq.#");
}
@Bean
public Binding binding06(){
return BindingBuilder
.bind(queue02())
.to(topicExchange())
.with("mq.#");
}
2.3 编写对应的请求方法
我们在controller类中编写对应的请求方法,我们使用参数的大大提高我们的测试灵活度
@RequestMapping("/send5")
public String send5(String rex) {
// 向交换机发送消息
amqpTemplate.convertAndSend("topicExchange",rex,"木易与馨月");
return "木易与馨月";
}
我们消费者因为在测试直连交换机是已经配置好了队列,因此不用重复设置直接测试。
测试
我们重新启动我们的生产者不用重新启动消费者,我们测试对应的接口方法。
进入Q1
进入Q2
进入Q1、Q2
3. 扇形交换机
3.1 创建主题交换机
// 创建fanout交换机
@Bean
public FanoutExchange fanoutExchange(){
return new FanoutExchange("fanoutExchange");
}
3.2 绑定队列及交换机的关系
// 绑定队列与交换机的关系
@Bean
public Binding binding07(){
return BindingBuilder
.bind(queue01())
.to(fanoutExchange());
}
@Bean
public Binding binding08(){
return BindingBuilder
.bind(queue02())
.to(fanoutExchange());
}
3.3 编写对应的请求方法
我们在controller类中写对应的w请求方法。
@RequestMapping("/send6")
public String send6() {
// 向交换机发送消息
amqpTemplate.convertAndSend("fanoutExchange","","木易Love馨月");
return "木易Love馨月";
}
测试
我们重新启动我们的生产者,在我们的网页上去访问对应的接口方法。
总结
我们今天的这期的博客分享中我们学习到了一些有关交换机的类型及其他们的基础使用,不同的交换机类型各自有着不同的特点及优点对应的应用场景也不同,我们在项目根据自身的需求情况采用对应的类型进行使用从此到达效果。我们下期博客主讲如何使用我们的死信交换机。
🎉🎉本期的博客分享到此结束🎉🎉
📚📚各位老铁慢慢消化📚📚
🎯🎯下期博客博主会带来新货🎯🎯
🎁三连加关注,阅读不迷路 !🎁