【RabbitMQ】交换机的概念及使用

一、引言

1、什么是交换机

        RabbitMQ中,交换机是一个核心概念,主要用来将生产者生产出来的消息,传送到对应的队列中。实际上,生产者生产的消息从不会直接发送到队列,而是发送到交换机。交换机一方面接收来自生产者的消息,另一方面将这些消息推入队列。

四种类型的交换机:

  1. 直连交换机(Direct exchange):根据消息携带的routing key 将消息传递给对应的队列,用来处理消息的单播路由。
  2. 扇形交换机(Fanout exchange):将消息路由给绑定到它身上的所有队列,不理会绑定的路由键,用来交换机处理消息的广播路由。
  3. 主题交换机(Topic exchange):通过对消息的路由键和队列到交换机的绑定模式之间的匹配,将消息路由给一个或多个队列,用来实现各种分发/订阅模式及其变种。
  4. 默认交换机:第一个参数是交换机的名称。无名(默认交换机)在之前的学习中,我们使用的都是默认的交换机,空字符串指名的就是默认的交换机,消息发送是通过router-key 找到指定的队列
    channel.basicPublish("",QUEUE_NAME,null,message.getBytes("UTF-8"));

2、Exchange

在RabbitMQ中,生产者发送消息不会直接将消息投递到队列中,而是先将消息投递到交换机中,在由交换机转发到具体的队列,队列再将消息以推送或者拉取方式给消费者进行消费

生产者将消息发送到Exchange,由Exchange再路由到一个或多个队列中

路由键(RoutingKey)
生产者将消息发送给交换机的时候,会指定RoutingKey指定路由规则。

绑定键(BindingKey)
通过绑定键将交换机与队列关联起来,这样RabbitMQ就知道如何正确地将消息路由到队列。

关系小结
生产者将消息发送给哪个Exchange是需要由RoutingKey决定的,生产者需要将Exchange与哪个队列绑定时需要由BindingKey决定的。

3、应用场景

RabbitMQ适用于各种需要异步通信、解耦、流量削峰、任务调度、日志处理等场景

  1. 异步处理:RabbitMQ通过消息队列实现异步处理,允许应用程序发送消息后继续执行其他任务,而不需要等待接收者的响应。这种异步性提高了系统的吞吐量和响应速度。
  2. 应用解耦:RabbitMQ用于在分布式系统中存储和转发消息,实现发送者和接收者之间的解耦。这有助于提高系统的可扩展性和可维护性。
  3. 流量削峰:RabbitMQ能够应对短时间内流量激增的情况,如秒杀、抢购等场景。通过使用RabbitMQ消息队列,可以避免后端应用被大量请求压垮。
  4. 任务调度:RabbitMQ可以用于任务调度,例如定时任务、批量任务等。通过将任务发送到消息队列中,可以实现任务的异步处理,同时可以通过设置消息的优先级、超时时间等属性来实现任务调度。
  5. 消息通知:在分布式系统中,各个模块之间需要相互通信,例如用户注册、支付成功、物流状态等。通过使用RabbitMQ,可以将消息发送到消息队列中,由消费者接收并处理,实现消息通知功能。
  6. 日志处理:RabbitMQ可以用于日志处理,例如日志收集、日志分析等。通过将日志消息发送到消息队列中,可以实现日志的异步处理,同时可以通过设置消息的属性、路由规则等来实现不同类型的日志处理。
  7. 跨系统通信:RabbitMQ可用于跨系统的异步通信,所有需要异步交互的地方都可以使用消息队列。在企业应用集成(EAI)中,文件传输、共享数据库、消息队列、远程过程调用都可以作为集成的方法。
  8. 应用内同步变异步:比如订单处理,就可以由前端应用将订单信息放到队列,后端应用从队列里依次获得消息处理,高峰时的大量订单可以积压在队列里慢慢处理掉。由于同步通常意味着阻塞,而大量线程的阻塞会降低计算机的性能。
  9. 消息驱动的架构(EDA):系统分解为消息队列和消息制造者和消息消费者,一个处理流程可以根据需要拆成多个阶段(Stage),阶段之间用队列连接起来,前一个阶段处理的结果放入队列,后一个阶段从队列中获取消息继续处理。
  10. 需要更灵活的耦合方式:如发布订阅,比如可以指定路由规则。
  11. 跨局域网甚至跨城市的通讯(CDN行业):比如北京机房与广州机房的应用程序的通信。

4、交换机类型

Ⅰ、直连交换机:Directexchange

直连交换机的路由算法非常简单:将消息推送到bindingkey与该消息的routingkey相同的队列。

        直连交换机X上绑定了两个队列。第一个队列绑定了绑定键orange,第二个队列有两个绑定键:black和green。

        在这种场景下,一个消息在布时指定了路由键为orange将会只被路由到队列Q1,路由键为black和green的消息都将被路由到队列Q2。其他的消息都将被丢失。

        同一个绑定键可以绑定到不同的队列上去,可以增加一个交换机X与队列Q2的绑定键,在这种情况下,直连交换机将会和广播交换机有着相同的行为,将消息推送到所有匹配的队列。一个路由键为black的消息将会同时被推送到队列Q1和Q2.

Ⅱ、主题交换机:Topic exchange

①、直连交换机的缺点

直连交换机的routing_key方案非常简单,如果我们希望一条消息发送给多个队列,那么这个交换机需要绑定上非常多的routing_key.假设每个交换机上都绑定一堆的routing_key连接到各个队列上。那么消息的管理就会异常地困难。

②、主题交换机的特点

发送到主题交换机的消息不能有任意的routingkey,必须是由点号分开的一串单词,这些单词可以是任意的,但通常是与消息相关的一些特征。

比如以下是几个有效的routingkey:"stock.usd.nyse","nyse.vmw","quick,orange,rabbit",routing key的单词可以有很多,最大限制是255 bytes。

Topic交换机的逻辑与direct交换机有点相似,使用特定路由键发送的消息将被发送到所有使用匹配绑定键绑定的队列,然而,绑定键有两个特殊的情况:

  • * 表示匹配任意一个单词
  • # 表示匹配任意一个或多个单词

routing key quick.orange.rabbit -> queue ql,Q2

routing key 1azy.orange.elephant -> queue Ql,Q2

routing key quick.orange.fox-> queue ?

routing keylazy.brown.fox-> queue ?

routing key lazy.pink.rabbit-> queue ?

routing key quick.brown.fox -> queue ?

③、延申

当一个队列的绑定键是”#",它将会接收所有的消息,而不再考虑所接收消息的路由键。
当一个队列的绑定键没有用到“#“和“*~时,它又像direct交换一样工作。

Ⅲ、扇形交换机:Fanout exchange

扇形交换机是最基本的交换机类型,它所能做的事情非常简单广播消息

扇形交换机会把能接收到的消息全部发送给绑定在自己身上的队列。因为广播不需要"思考”,所以扇形交换机处理消息的速度也是所有的交换机类型里面最快的。

Ⅳ、首部交换机:Headersexchange

首部交换机和扇形交换机都不需要路由键routingKey,交换机时通过Headers头部来将消息映射到队列的,有点像HTTP的Headers。

Hash结构中要求携带一个键"x-match",这个键的Value可以是any或者al,这代表消息携带的Hash是需要全部匹配(al),还是仅匹配一个键(any)就可以了

相比直连交换机,首部交换机的优势是匹配的规则不被限定为字符串(string)而是Object类型。

  • all:在发布消息时携带的所有Entry必须和绑定在队列上的所有Entry完全匹配。
  • any:只要在发布消息时携带的有一对键值对headers满足队列定义的多个参数arguments的其中一个就能匹配上,注意这里是键值对的完全匹配,只匹配到键了,值却不一样是不行的;

Ⅴ、默认交换机

实际上是一个由RabbitMQ预先声明好的名字为空字符串的直连交换机(direct exchange)。

它有一个特殊的属性使得它对于简单应用特别有用处:那就是每个新建队列(queue)都会自动绑定到默认交换机上,绑定的路由键(routing key)名称与队列名称相同。

①、绑定
默认交换

默认交换隐式绑定到每个队列,路由键等于队列名称。无法显式绑定到默认交换或取消绑定畎认交换。它也无法删除

②、Bindings(1)

当你声明了一个名为"hello”的队列,RabbitMQ会自动将其绑定到默认交换机上,绑定(binding)的路由键名称也是为"hello”。当携带着名为"helo”的路由键的消息被发送到默认交换机的时候,此消息会被默认交换机路由至名为"hello”的队列中类似amq.*的名称的交换机:这些是RabbitMQ默认创建的交换机。这些队列名称被预留做RabbitMQ内部使用,不能被应用使用,否则抛出403(ACCESS_REFUSED)错误

Ⅵ、Dead Letter Exchange(死信交换机)

        RabbitMQ中的一个重要概念,也有人称之为死信邮箱。当消息在一个队列中由于过期、被拒绝等原因变成死信之后,它会被重新发送到另一个交换机中,这个交换机就是死信交换机。绑定死信交换机的队列则被称为死信队列

当消息在一个正常的队列中变成死信后,RabbitMQ会自动将这个消息重新发布到设置的死信交换机上,进而被路由到另一个队列,即死信队列。在RabbitMQ中,死信交换机和一般的交换机没有区别,它可以在任何队列上被指定。

使用死信队列时,只需要在定义队列的时候设置队列参数“x-dead-letter-exchange”来指定交换机即可。通过这种方式,可以实现一些特殊的应用逻辑,例如重试机制、日志审计等。

消息变成死信一般是以下三种情况:

  • 消息被拒绝,并且设置 requeue 参数为 false
  • 消息过期(默认情况下 Rabbit 中的消息不过期,但是可以设置队列的过期时间和消息的过期时间以达到消息过期的效果)
  • 队列达到最大长度(一般当设置了最大队列长度或大小并达到最大值时)

当满足上面三种情况时,消息会变成死信消息,并通过死信交换机投递到相应的队列中。

我们只需要监听相应队列,就可以对死信消息进行最后的处理。

5、交换机的属性

Name:交换机名称

Type:交换机类型,direct、topic、fanout、headers

Durabi1ity:是否需要持久化,如果持久性,则RabbitMQ重启后,交换机还存在

Auto Delete:当最后一个绑定到Exchange上的队列删除后,自动删除该

ExchangeInterna1:当前Exchange是否用于RabbitMQ内部使用,默认为False

Arguments:扩展参数,用于扩展AMQP协议定制化使用

二、实操案例

1、准备

消费者

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 {// 接收directExchange01交换机中Queue01队列消息的方法@RabbitHandlerpublic void Queue01(String msg) {log.warn("Queue01,接收到信息:" + msg);}}
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 {// 接收directExchange01交换机中Queue02队列消息的方法@RabbitHandlerpublic void Queue02(String msg) {log.warn("Queue02,接收到信息:" + msg);}}

2、直连

RabbitConfig

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;@Configuration
@SuppressWarnings("all")
public class RabbitConfig {//    直连交换机/*** 直连交换机* /* 创建两个Binding Bean,分别与Queue01和Queue02队列进行绑定* 并都指向directExchange01(直连交换机),键分别为Key01和Key02*///  创建队列@Beanpublic Queue Queue01() {return new Queue("Queue01");}@Beanpublic Queue Queue02() {return new Queue("Queue02");}// 创建直连(direct)交换机@Beanpublic DirectExchange directExchange01() {return new DirectExchange("directExchange01");}// 创建Binding Bean,与Queue01和directExchange01绑定,键为Key01@Beanpublic Binding binding01() {return BindingBuilder.bind(Queue01()).to(directExchange01()).with("Key01");}// 创建Binding Bean,与Queue02和directExchange01绑定,键为Key02@Beanpublic Binding binding02() {return BindingBuilder.bind(Queue02()).to(directExchange01()).with("Key02");}}

Controller层编写

    // 自动装配rabbitTemplate@Autowiredprivate AmqpTemplate rabbitTemplate;@RequestMapping("test03")public String test03() {// 发送消息到名为directExchange01的交换机,路由键为key01,信息内容为:Hello, direct exchange!// 这里的directExchange01是RabbitMQ中定义的交换机名称// 这里的key01是RabbitMQ中定义的路由键名称rabbitTemplate.convertAndSend("directExchange01", "Key01", "Hello, direct exchange!");return "👌";}@RequestMapping("test04")public String test04() {// 发送消息到名为directExchange01的交换机,路由键为key02,信息内容为:Hello, direct exchange!// 这里的directExchange01是RabbitMQ中定义的交换机名称// 这里的key02是RabbitMQ中定义的路由键名称rabbitTemplate.convertAndSend("directExchange01", "Key02", "Hello, direct exchange!");return "👌";}

测试结果

消费者接收的

3、主题

RabbitConfig

 /*** 主题交换机* /* binding03:将Queue01绑定到topicExchange,并使用*.*.Q1作为路由键。* binding04:将Queue02绑定到topicExchange,并使用*.*.Q2作为路由键。* binding05:将Queue01绑定到topicExchange,并使用un.#作为路由键。* binding06:将Queue02绑定到topicExchange,并使用un.#作为路由键。*  '*'代表一个单词,*  '#'代表任意数量的字符,也代表0个或多个*/// 创建主题交换机@Beanpublic TopicExchange topicExchange() {return new TopicExchange("topicExchange");}@Beanpublic Binding binding03() {return BindingBuilder.bind(Queue01()).to(topicExchange()).with("*.*.Q1");}@Beanpublic Binding binding04() {return BindingBuilder.bind(Queue02()).to(topicExchange()).with("*.*.Q2");}@Beanpublic Binding binding05() {return BindingBuilder.bind(Queue01()).to(topicExchange()).with("un.#");}@Beanpublic Binding binding06() {return BindingBuilder.bind(Queue02()).to(topicExchange()).with("un.#");}

Controller层

   @RequestMapping("test05")public String test05(String rex) {// 向topicExchange发送消息,路由键为rex,消息内容为"Hello,topicExchange:Queue!"rabbitTemplate.convertAndSend("topicExchange", rex, "Hello,topicExchange:Queue!");return "🐉";}

4、扇形

RabbitConfig

/*** 扇形交换机** 定义了一个FanoutExchange,加上Bean注解* 定义了两个Binding,加上Bean注解* 将两个队列绑定到FanoutExchange上,从而实现广播消息的功能* 扇形交换机会将接收到的消息路由到所有绑定到它上的队列。*/// 创建扇形交换机// 创建一个FanoutExchange类型的bean@Beanpublic FanoutExchange fanoutExchange() {return new FanoutExchange("fanoutExchange");}// 创建一个绑定,将Queue01绑定到fanoutExchange@Beanpublic Binding binding07() {return BindingBuilder.bind(Queue01()).to(fanoutExchange());}// 创建一个绑定,将Queue02绑定到fanoutExchange@Beanpublic Binding binding08() {return BindingBuilder.bind(Queue02()).to(fanoutExchange());}

Controller

    @RequestMapping("test06")public String test06() {// 向fanoutExchange发送消息,消息内容为"Hello,fanoutExchange:Queue!"rabbitTemplate.convertAndSend("fanoutExchange","","Hello,fanoutExchange:Queue!");return "👌🐉";}

测试结果

三、总结

直连交换机适用于简单的单播路由场景。

主题交换机适用于实现各种分发/订阅模式及其变种。

扇形交换机则适用于需要快速广播消息的场景。

直连交换机

  • 特点:直连交换机的routing_key方案非常简单。如果我们希望一条消息发送多个队列,那么这个交换机需要绑定上非常多的routing_key假设每个交换机上都绑定一堆的routing_key连接到各个队列上,那么消息管理就会异常困难
  • 应用场景:适用于简单的消息路由场景,如单播路由。

主题交换机

  • 特点:主题交换机根据消息的routing key和队列到交换机的绑定模式之间的匹配,将消息路由给一个或多个队列。这提供了多种分发/订阅模式及其变种。
  • 应用场景:适用于实现各种分发/订阅模式及其变种,例如多播路由、广播路由等。

扇形交换机

  • 特点:扇形交换机是最基本的交换机类型,其功能非常简单——广播消息。它会把能接收到的消息全部发送给绑定在自己身上的队列,因为广播不需要“思考”,所以扇形交换机处理消息的速度也是所有的交换机类型里面最快的。此外,扇形交换机没有路由键概念,即使绑定了路由键也会被无视
  • 应用场景:适用于需要快速广播消息的场景,例如日志处理、事件通知等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/651789.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot项目配置SSL后,WebSocket连接失败的解决方案

SpringBoot项目配置SSL后,WebSocket连接应使用wss协议,而不是ws协议。在前端配置WebSocket时,URL以wss://开头。

嵌入式学习第十二天

8.数组指针和指针数组(2): (1)指针数组: int *a[5]; char *str[5]; 指针数组主要用来操作字符串数组,通过将指针数组的每个元素存放字符串的首地址实现对多个字符串的操作 二维数组主要用来存储字符串数组…

Nodejs前端学习Day3_准备工作

妈的,这几天真tm冷,前天上午还下了一整天的雪,大雪 文章目录 前言一、Node.js简介1.1何为1.2有什么 二、Node.js可以做什么三、学习路线四、下载nodejs4.1小坑记录4.2LTS和Current版本的不同 五、什么是终端六、在nodejs中执行js代码七、powe…

Kubernetes(K8S)各种攻击方法

1. 准备工作 1.1. metarget使用 项目地址(教程):https://github.com/Metarget/metarget/blob/master/README-zh.md 注意:推荐在Ubuntu 18.04(推荐)安装。 1.1.1. 安装metarget git clone https://github.com/Metarget/metarget.git cd metarget/ sudo apt install pyt…

[BUUCTF]-PWN:hitcon2014_stkof解析

又是一道堆题,先看保护 关键信息,64位,没开pie。再看ida 大致就是alloc创建堆块,free释放堆块,fill填充堆块内容,以及一个看起来没啥用的函数,当然我也没利用这个函数去解题 这里有两种解法 解…

【VB测绘程序设计】案例8——IF选择结构练习排序(附源代码)

【VB测绘程序设计】案例6——IF选择结构练习排序(附源代码) 文章目录 前言一、界面显示二、程序说明三、程序代码四、数据演示总结前言 本文主要掌握Val()函数转换,inputBox函数、IF条件句的练习,输入3个数,按大到小排序并打印。 一、界面显示 二、程序说明 利用inpu…

[BJDCTF2020]The mystery of ip

hint 猜测ip和XFF有关 加一个XFF 下面这一步是看了wp出来的:存在ssti 这里尝试用jinja的注入方法,页面回显了是php的smarty框架 查了一下smarty的注入方法,发现可以直接执行php命令 在根目录找到flag

RPC教程 6.负载均衡

1.负载均衡策略 假设有多个服务实例,而每个实例都提供相同的功能,为了提高整个系统的吞吐量,每个实例部署在不同的机器上。客户端可以选择任意一个实例进行调用,获取想要的结果。那如何选择呢?取决于负载均衡的策略。…

Visual Studio如何修改成英文版

1、打开 Visual Studio Installer 2、点击修改 3、找到语言包,选择需要的语言包,而后点击修改 4、等待下载 5、 安装完成后启动Visual Studio 6、在工具-->选项-->环境-->区域设置-->English并确定 7、重启 Visual Studio,配置…

数据结构与算法教程,数据结构C语言版教程!(第六部分、数据结构树,树存储结构详解)三

第六部分、数据结构树,树存储结构详解 数据结构的树存储结构,常用于存储逻辑关系为 "一对多" 的数据。 树存储结构中,最常用的还是二叉树,本章就二叉树的存储结构、二叉树的前序、中序、后序以及层次遍历、线索二叉树、…

QT实现USB摄像头接入显示

一、UVC协议简介 UVC全称是USB Video Class(USB视频类),是一种标准化的USB视频设备通信协议,它定义了摄像头与主机之间的数据传输协议和格式。 UVC协议的出现,解决了摄像头厂商之间互不兼容,以及摄像头应…

(二十八)ATP应用测试平台——使用electron集成vue3桌面应用程序

前言 Electron 是一个开源的框架,它允许使用 Web 技术(HTML、CSS 和 JavaScript)构建跨平台的桌面应用程序。通过 Electron,开发者可以使用前端技术栈来创建具有原生应用程序体验的桌面应用。 Electron可以在 Windows、Mac 和 L…

【前端web入门第一天】02 HTML图片标签 超链接标签 音频标签 视频标签

文章目录: 1.HTML图片标签 1.1 图像标签-基本使用1.2 图像标签-属性1.3 路径 1.3.1 相对路径 1.3.2 绝对路径 2.超链接标签 3.音频标签 4.视频标签 1.HTML图片标签 1.1 图像标签-基本使用 作用:在网页中插入图片。 <img src"图片的URL">src用于指定图像…

HCS 华为云Stack产品组件

HCS 华为云Stack产品组件 Cloud Provisioning Service(CPS) 负责laas的云平台层的部署和升级是laas层中真正面向硬件设备&#xff0c;并将其池化软件化的部件。 Service OM 资源池(计算/存储/网络)以及基础云服务(ECS/EVS/PC)的管理工具。 ManageOne ManageOne包括服务中心…

数据结构(1)--> 顺序表

定义&#xff1a; 顺序表存储定义&#xff1a; 把逻辑上相邻的数据元素存储在物理上相邻的存储单元中的存储结构&#xff0c;顺序表功能的实现借助于数组&#xff0c;通过对数组进行封装&#xff0c;从而实现增删查改的功能&#xff0c;严格意义上来说&#xff08;数组无法实现…

第六课:Prompt

文章目录 第六课&#xff1a;Prompt1、学习总结&#xff1a;Prompt介绍预训练和微调模型回顾挑战 Pre-train, Prompt, PredictPrompting是什么?prompting流程prompt设计 课程ppt及代码地址 2、学习心得&#xff1a;3、经验分享&#xff1a;4、课程反馈&#xff1a;5、使用Mind…

由两个有限项的等差数列B, C, 求有多少个有限项的等差数列A,满足C是A, B的所有公共项,若有无穷个A满足条件,输出-1

题目 思路&#xff1a; #include <bits/stdc.h> using namespace std; #define int long long #define pb push_back const int maxn 1e6 5, inf 1e9 5, maxm 4e4 5, mod 1e9 7, N 1e6; // int a[maxn], b[maxn]; int n, m; string s;int qpow(int a, int b){i…

Unity中创建Ultraleap 3Di交互项目

首先&#xff0c;创建新的场景 1、创建一个空物体&#xff0c;重命名为【XP Leap Provider Manager】&#xff0c;并在这个空物体上添加【XR Leap Provider Manager】 在物体XP Leap Provider Manager下&#xff0c;创建两个子物体Service Provider(XR)和Service Provider(…

C语言与操作符相关的经典例题

目录 一道变态的面试题&#xff1a;不能创建临时变量&#xff08;第三个变量&#xff09;&#xff0c;实现两个数的交换。 编写代码实现&#xff1a;求一个整数存储在内存中的二进制中1的个数。 二进制位置0或者置1 如果以下的知识点不是很清楚的可以去看这篇文章&#xff1…

阿里云部署配置幻兽帕鲁Palworld联机服务器详细教程

阿里云作为国内领先的云计算服务提供商&#xff0c;为企业和个人提供了丰富的云服务。本文将为大家详细介绍如何在阿里云上配置幻兽帕鲁Palworld联机服务器&#xff0c;以便与更多玩家共同体验游戏的乐趣。 第一步&#xff1a;登录服务器创建页 1、进入幻兽帕鲁联机服务快速部…