Rabbitmq面试题总结,非常详细,杜绝标题党,不详细你打我,下次不写博客了
Posted 梵高的猪v
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Rabbitmq面试题总结,非常详细,杜绝标题党,不详细你打我,下次不写博客了相关的知识,希望对你有一定的参考价值。
文章目录
- 说明
- 什么是MQ?
- MQ有什么缺点?
- MQ的应用场景
- Rabbitmq是什么?Rabbitmq的构造
- vhost 是什么? 起什么作用?
- 谈谈你对AMQP的理解
- 消息是如何路由的?
- Rabbitmq里的交换机类型有哪些,都有什么区别?什么是交换机?
- 消息传输的模式有哪些?
- 消息分发策略有哪些?
- rabbitmq消息处理流程,即生产者生产消息,消费者接收消息过程
- 如何保证消息的顺序消费
- 消息队列如何保证消息的可靠性传输
- 如何处理消息堆积情况?
- 如何保证消息队列的高可用?
- 说说你对rabbitmq持久化机制的理解
- 可以对所有消息都持久化吗?
- 说说你对rabbitmq应答机制的理解
- 说一下Rabbitmq事务机制
- 谈谈你对死信队列的理解
- 说一下消息确认机制和返回机制,有什么区别?
- 让你来设计一个消息队列,你会怎么设计
- SpringBoot如何整合Rabbitmq
说明
我为什么要做这么一个总结,虽然我也是个小白,最近也在找工作,但是我们上网看面试题的时候,发现这些所谓的大博主,什么狗屁新星,没一点原创,抄的可怕,标明原创也就算了,还一字不漏的给抄下来,有些一字不漏的复制我理解,因为大伙说法都那样,但是有些说不明白也一顿乱抄,真不知道他们自己看懂了没有,又搬了过去.“我并不反对借鉴,但也得有个度,当你搜索一个面试题发现几页下来都是一模一样,内容一样,标点一样的时候,你会怎么样?”
我也是总结的,非原创,因为知识都是一样,学习过来的,并且我这章内容里也有一些照抄的,那是因为我发现这个说法我已经不需要总结了,已经说的非常好了,我复制过来,并标明出处.但是有一些是我自己看了很多篇总结而来的,或者看视频看过来的,我就不标明出处了.
注意:我和其他博主不同的是,我会标明出处!!希望大家以后抄的时候也要抄个出处,网络的环境是大伙公共维护的.
什么是MQ?
mq是一个消息队列,其主要目的是为了解决传统的消息传输上管理困难,效率不高的问题.
mq有三大优点:解耦,异步,削峰.
解耦: 如果是传统的消息通讯方式,无论是哪一方都要去维护一份供外部通讯的这个一个接口,而且各方处理消息的能力有限,效率上明显跟不上,并且这样子二者之间的耦合度非常高,对于拓展管理方面极不友好,而是要了mq就不一样,发送方只需要将消息发送给mq就可以了,别的不用考虑,接口什么的由mq去维护,接收方也只需要去mq里消费消息就可以了,就需要其他开销,一切由mq中间件来做,达到了解耦操作.
异步: 使用mq,还可以达到异步效果,极大地提升了消息传输的效率.发送方在发送消息后不需要关心消费方是否能消费完成,还可以继续发送其他消息.
削峰:如果是传统的消息通讯,一下子有大量的消息发送给接收方,这样对于接收方的处理压力是很大的,而我们刚好可以利用mq达到一个缓冲操作,一旦流量超出了接收方处理范围,不用担心,只需要慢慢消费即可,像经典的双十一,就很容易会使用到mq这么一个优点.
虽然mq有三大优点,但是我们还是得关心其一些缺点:
因为增加了中间件,系统复杂度肯定大大提高,增加了很多维护的成本,比如我们要保证消息不丢失(一致性)和消息幂等性问题,还要保证mq的高可用等.
MQ有什么缺点?
系统可用性降低
系统引入的外部依赖越多,越容易挂掉,本来你就是A系统调用BCD三个系统的接口就好了,人 ABCD四个系统好好的,没啥问题,你偏加个MQ进来,万一MQ挂了咋整?MQ挂了,整套系统崩溃了,你不就完了么。(可以利用集群解决)
系统复杂性提高
硬生生加个MQ进来,你怎么保证消息没有重复消费?怎么处理消息丢失的情况?怎么保证消息传递的顺序性?头大头大,问题一大堆,痛苦不已。
一致性问题(保证消息不丢失)
A系统处理完了直接返回成功了,人都以为你这个请求就成功了;但是问题是,要是BCD三个系统那里,BD两个系统写库成功了,结果C系统写库失败了,咋整?你这数据就不一致了。
所以消息队列实际是一种非常复杂的架构,你引入它有很多好处,但是也得针对它带来的坏处做各种额外的技术方案和架构来规避掉,最好之后,你会发现,妈呀,系统复杂度提升了一个数量级,也许是复杂了10倍。但是关键时刻,用,还是得用的。
————————————————
版权声明:本文为CSDN博主「Java小叮当」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_48795607/article/details/116064045
MQ的应用场景
2.1异步处理
场景说明:用户注册后,需要发注册邮件和注册短信。传统的做法有两种1.串行的方式;2.并行方式。
(1)串行方式:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户端。(架构KKQ:466097527,欢迎加入)
(2)并行方式:将注册信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间。
假设三个业务节点每个使用50毫秒钟,不考虑网络等其他开销,则串行方式的时间是150毫秒,并行的时间可能是100毫秒。
因为CPU在单位时间内处理的请求数是一定的,假设CPU1秒内吞吐量是100次。则串行方式1秒内CPU可处理的请求量是7次(1000/150)。并行方式处理的请求量是10次(1000/100)。
小结:如以上案例描述,传统的方式系统的性能(并发量,吞吐量,响应时间)会有瓶颈。如何解决这个问题呢?
引入消息队列,将不是必须的业务逻辑,异步处理。改造后的架构如下:
按照以上约定,用户的响应时间相当于是注册信息写入数据库的时间,也就是50毫秒。注册邮件,发送短信写入消息队列后,直接返回,因此写入消息队列的速度很快,基本可以忽略,因此用户的响应时间可能是50毫秒。因此架构改变后,系统的吞吐量提高到每秒20 QPS。比串行提高了3倍,比并行提高了两倍。
2.2应用解耦
场景说明:用户下单后,订单系统需要通知库存系统。传统的做法是,订单系统调用库存系统的接口。如下图:(架构KKQ:466097527,欢迎加入)
传统模式的缺点:
1) 假如库存系统无法访问,则订单减库存将失败,从而导致订单失败;
2) 订单系统与库存系统耦合;
如何解决以上问题呢?引入应用消息队列后的方案,如下图:
订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功。
库存系统:订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作。
假如:在下单时库存系统不能正常使用。也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。实现订单系统与库存系统的应用解耦。
2.3流量削锋
流量削锋也是消息队列中的常用场景,一般在秒杀或团抢活动中使用广泛。
应用场景:秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。
可以控制活动的人数;
可以缓解短时间内高流量压垮应用;
用户的请求,服务器接收后,首先写入消息队列。假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面;
秒杀业务根据消息队列中的请求信息,再做后续处理。
2.4日志处理
日志处理是指将消息队列用在日志处理中,比如Kafka的应用,解决大量日志传输的问题。架构简化如下:
日志采集客户端,负责日志数据采集,定时写受写入Kafka队列;
Kafka消息队列,负责日志数据的接收,存储和转发;
日志处理应用:订阅并消费kafka队列中的日志数据;
(1)Kafka:接收用户日志的消息队列。
(2)Logstash:做日志解析,统一成JSON输出给Elasticsearch。
(3)Elasticsearch:实时日志分析服务的核心技术,一个schemaless,实时的数据存储服务,通过index组织数据,兼具强大的搜索和统计功能。
(4)Kibana:基于Elasticsearch的数据可视化组件,超强的数据可视化能力是众多公司选择ELK stack的重要原因。
2.5消息通讯
消息通讯是指,消息队列一般都内置了高效的通信机制,因此也可以用在纯的消息通讯。比如实现点对点消息队列,或者聊天室等。
点对点通讯:
客户端A和客户端B使用同一队列,进行消息通讯。
聊天室通讯:
客户端A,客户端B,客户端N订阅同一主题,进行消息发布和接收。实现类似聊天室效果。
以上实际是消息队列的两种消息模式,点对点或发布订阅模式。模型为示意图,供参考。
————————————————
版权声明:本文为CSDN博主「许文强」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_16824623/article/details/78901953
Rabbitmq是什么?Rabbitmq的构造
(1)生产者Publisher:生产消息,就是投递消息的一方。消息一般包含两个部分:消息体(payload)和标签(Label)
(2)消费者Consumer:消费消息,也就是接收消息的一方。消费者连接到RabbitMQ服务器,并订阅到队列上。消费消息时只消费消息体,丢弃标签。
(3)Broker服务节点:表示消息队列服务器实体。一般情况下一个Broker可以看做一个RabbitMQ服务器。
(4)Queue:消息队列,用来存放消息。一个消息可投入一个或多个队列,多个消费者可以订阅同一队列,这时队列中的消息会被平摊(轮询)给多个消费者进行处理。
(5)Exchange:交换器,接受生产者发送的消息,根据路由键将消息路由到绑定的队列上。
(6)Routing Key: 路由关键字,用于指定这个消息的路由规则,需要与交换器类型和绑定键(Binding Key)联合使用才能最终生效。
(7)Binding:绑定,通过绑定将交换器和队列关联起来,一般会指定一个BindingKey,通过BindingKey,交换器就知道将消息路由给哪个队列了。
(8)Connection :网络连接,比如一个TCP连接,用于连接到具体broker
(9)Channel: 信道,AMQP 命令都是在信道中进行的,不管是发布消息、订阅队列还是接收消息,这些动作都是通过信道完成。因为建立和销毁 TCP 都是非常昂贵的开销,所以引入了信道的概念,以复用一条 TCP 连接,一个TCP连接可以用多个信道。客户端可以建立多个channel,每个channel表示一个会话任务。
(10)Message:消息,由消息头和消息体组成。消息体是不透明的,而消息头则由一系列的可选属性组成,这些属性包括routing-key(路由键)、priority(相对于其他消息的优先权)、delivery-mode(指出该消息可能需要持久性存储)等。
(11)Virtual host:虚拟主机,用于逻辑隔离,表示一批独立的交换器、消息队列和相关对象。一个Virtual host可以有若干个Exchange和Queue,同一个Virtual host不能有同名的Exchange或Queue。最重要的是,其拥有独立的权限系统,可以做到 vhost 范围的用户控制。当然,从 RabbitMQ 的全局角度,vhost 可以作为不同权限隔离的手段
————————————————
版权声明:本文为CSDN博主「张维鹏」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/a745233700/article/details/115060109
vhost 是什么? 起什么作用?
每一个rabbitmq服务器都能创建虚拟的消息服务器,我们称之为虚拟主机(virtual host)。简称vhost
特性:
每一个vhost本质上是一个小型的独立的rabbitmq服务器,拥有自己独立的完整的一套队列、绑定关系、交换器等。同一个服务器上的多个vhost是完全隔离的。队列及交换器等不互通。
所以一个broker可以开设多个vhost,用于不同用户的权限分离
如何创建vhost?
1)通过前台页面的admin中创建
2)使用rabbitmqctl add_vhost vhost名称 命令
如何删除vhost?
1)前台删除
2)rabbitmqctl delete_vhost vhost_name
————————————————
版权声明:本文为CSDN博主「whale4fly」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/w_zhendefu/article/details/110454102
谈谈你对AMQP的理解
AMQP协议是什么?
AMQP协议,所谓的高级消息队列协议,可以把它理解成一种公认的协议规范,就像http协议一样,只是这个AMQP协议针对的是消息队列。这个协议使得遵从了它的规范的客户端应用和消息中间件服务器的全功能互操作成为可能。
了解下AMQP协议的基本概念
Broker:用于接受信息和分发信息的应用。
Virtual hosts:在一个Broker上面划分出多个隔离的环境,这多个环境就可以理解成是Virtual hosts,就像使用虚拟机一样,每个虚拟机之间都有完整的组件,各Virtual hosts下的用户、交换器以及队列等互不影响,这样方便不同的业务团队在使用同一个Rabbit server提供的服务时,能够划清界限。
Connection:消息生产者和消息消费者还有Broker之间的TCP连接,如果要断开连接,只会在客户端断开,而
Broker不会断开连接,除非网络出现了故障或者Broker服务出了问题。
Channel:通道,如果每一次访问消息队列中间件都建立一个TCP连接的话,那么系统资源会被大量的占用,效率也会降低,所以AMQP提供了Channel机制,共享同一个TCP连接,而一个TCP连接里可以有大量的Channel。假设如果有多个线程访问消息队列中间件服务,每个线程通常都会有自己单独的Channel来做通信,而每个Channel会有自己的Channel id,这样客户端和Broker就能够互相识别Channel,所以Channel之间是完全隔离的。
Exchange:交换机,这是消息到达Broker的第一站,由于Exchange和Queues之间有绑定键来确定双方发送消息的匹配规则,所以这时Exchange会根据消息的路由键和自己的类型,来匹配绑定规则,将消息分发到对应的Queues上。
Queue:队列,消息所到达的最终站,消费者从这里拿消息做消费。
Binding:可以把它理解成一个虚拟的连接,定义了Exchange和Queues之间的匹配规则,只有匹配这个规则的交换机里的消息才会被发送到这个队列里,不过如果消息没法找到匹配的队列的话,那么,根据该条消息的属性,这个消息要么被丢弃,要么返回生产者那里。
Exchange的类型
这里把Exchange和Queues之间的匹配规则称之为绑定键。
类型 说明
direct(直连) 消息的路由键要和绑定键一模一样才能分发到队列
fanout(广播) 无需绑定键,只要有和这个交换机做绑定的队列,都会收到消息,有点类似发布-订阅
topic(主题) 这个类型的交换机要求消息路由键和绑定键要模糊匹配才能分发,以.号来分割每个词, #号代表匹配多个词,*号代表匹配只一个词
headers(header属性) 这种类型的交换机不再是基于路由键了,而是基于消息中的header属性,只有消息中header属性的值与绑定键相同时,消息才会被分发到相应的队列中
默认交换机 如果不指定上述的交换机类型,就会使用默认的direct类型,同时绑定键默认是队列名,所以消息会分发到与路由键同名的队列里
————————————————
版权声明:本文为CSDN博主「绅士jiejie」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_38106322/article/details/104862947
消息是如何路由的?
消息提供方->路由->一至多个队列消息发布到交换器时,消息将拥有一个路由键(routing key),在消息创建时设定。通过队列绑定键,可以把队列绑定到交换器上。消息到达交换器后,RabbitMQ 会将消息的路由键与队列的绑定键进行匹配(针对不同的交换器有不同的路由规则);
常用的交换器主要分为一下三种(广播模式):
fanout:如果交换器收到消息,将会广播到所有绑定的队列上
direct:如果路由键完全匹配,消息就被投递到相应的队列
topic:可以使来自不同源头的消息能够到达同一个队列。 使用 topic 交换器时,可以使用通配符
————————————————
版权声明:本文为CSDN博主「Java小叮当」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_48795607/article/details/116064045
Rabbitmq里的交换机类型有哪些,都有什么区别?什么是交换机?
1、什么是Exchange
在RabbitMQ中,生产者发送消息不会直接将消息投递到队列中,而是先将消息投递到交换机中, 在由交换机转发到具体的队列, 队列再将消息以推送或者拉取方式给消费者进行消费.
2、路由键 ( RoutingKey)
生产者将消息发送给交换机的时候, 会指定RoutingKey指定路由规则。
3、绑定键 ( BindingKey)
通过绑定键将交换机与队列关联起来, 这样RabbitMQ就知道如何正确地将消息路由到队列。
4、关系
生产者将消息发送给哪个Exchange是需要由RoutingKey决定的,生产者需要将Exchange与哪个队列绑定时需要由 BindingKey决定的。
二、交换机类型和区别
1、直连交换机: Direct exchange
直连交换机的路由算法非常简单: 将消息推送到binding key与该消息的routing key相同的队列。
直连交换机X上绑定了两个队列。第一个队列绑定了绑定o键range, 第二个队列有两个绑定键: black和green。
在这种场景下,一 个消息在布时指定了路由键为orange将会只被路由到队列Q1 I 路由键为black 和green的消息都将被路由到队列Q2。其他的消息都将被丢失。
同一个绑定键可以绑定到不同的队列上去, 可以增加一个交换机X与队列Q2的绑定键,在这种清况下,直连交换机将会和广播交换机有着相同的行为, 将消息推送到所有匹配的队列。一个路由键为black的消息将会同时被推送到队列Q1和Q2。
2、 主题交换机: Topic exchange
直连交换机的缺点:
直连交换机的 routing_key方案非常简单 ,如果我们希望一 条消息发送给多个队列 ,那么这个交换机需 要绑定上非常多的 routing_key.
假设每个交换机上都绑定一堆的 routing_key连接到各个队列上。那么消息的管理 就会异常地困难。
主题交换机的特点:
发送到主题交换机的 消息不能有任意的 routing key, 必须是由点号分开的一串单词,这些单词可以是任意的,但通常是与消息相关的一些特征。
如以下是几个有效的routing key:
“stock.usd.nyse”, “nyse.vmw”, “quick.orange.rabb 代”, routing key的单词可以 有很多,最大限制是255 bytes。
Topic 交换机的 逻辑与 direct 交换机有点 相似 使用特定路由键发送的消息 将被发送到所有使用匹配绑定键绑定的队列 ,然而 ,绑定键有两个特殊的情况:
*表示匹配任意一个单词
#表示匹配任意—个或多个单词
如:
routing key quick.orange.rabbit-> queue Ql, Q2
routing key lazy.orange.elephant-> queue Ql,Q2
延申:
当一个队列的绑定键是"#",它将会接收所有的消息,而不再考虑所接收消息的路由键。
当一个队列的绑定键没有用到"#"和’*"时,它又像 direct 交换一样工作。
2、扇形交换机: Fanout exchange
扇形交换机是最基本的交换机类型,它所能做的事清非常简单广播消息。
扇形交换机会把能接收到的消息全部发送给绑定在自己身上的队列。因为广播不需要'思考”,所以扇形交换机处理消息的速度也是所有的交换机类型里面最快的。
3、首部交换机: Headers exchange
类似主题交换机,但是头交换机使用多个消息属性来代替路由键建立路由规则。通过判断消息头的值能否与指定的绑定相匹配来确立路由规则。
此交换机有个重要参数:”x-match”
当”x-match”为“any”时,消息头的任意一个值被匹配就可以满足条件
当”x-match”设置为“all”的时候,就需要消息头的所有值都匹配成功
————————————————
版权声明:<<首部交换机>>为CSDN博主「hry2015」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/hry2015/article/details/79118804
4、默认交换机
实际上是— 个由 RabbitMQ预先声明好的名字为空字符串的直连交换机 (direct exchange) 。
它有一个特殊的属性使得它对于简单应用特别有用处 :那就是每个新建队列 (queue) 都会自动绑定到默认交换机上,绑定的 路由键(routing key) 名称与队列名称相同。
当你声明了一个名为“hello”的队列,RabbitMQ会自动将其绑定到默认交换机上,绑定(binding)的路由键名称也是为“hello”。
当携带着名为“hello”的路由键的信息被发送到默认交换机的时候,此消息会被默认交换机路由至名为“hello”的队列中
类似amq.*的名称的交换机:这些是RabbitMQ默认创建的交换机。
这些队列名称被预留做RabbitMQ内部使用,不能被应用使用,否则抛出403错误
5、Dead Letter Exchange(死信交换机)
演示链接:https://blog.csdn.net/weixin_60389087/article/details/123167193
RabbitMQ作为一个高级消息中间件,提出了死信交换器的概念。
这种交互器专门处理死了的信息(被拒绝可以重新投递的信息不能算死的)。
消息变成死信一般是以下三种情况:
①、消息被拒绝,并且设置requeue参数为false。
②、消息过期(默认情况下Rabbit中的消息不过期,但是可以设置队列的过期时间和信息的过期的效果)
③、队列达到最大长度(一般当设置了最大队列长度或大小并达到最大值时)
当满足上面三种情况时,消息会变成死信消息,并通过死信交换机投递到相应的队列中。
我们只需要监听相应队列,就可以对死信消息进行最后的处理。
订单超时处理:
生产者生产一条1分钟后超时的订单信息到正常交换机exchange-a中,消息匹配到队列queue-a,但一分钟后仍未消费。
消息会被投递到死信交换机dlx-exchange中,并发送到私信队列中。
死信队列dlx-queue的消费者拿到信息后,根据消息去查询订单的状态,如果仍然是未支付状态,将订单状态更新为超时状态。
6.交换机的属性
Name:交换机名称
Type:交换机类型,direct,topic,fanout,headers
Durability:是否需要持久化,如果持久性,则RabbitMQ重启后,交换机还存在
Auto Delete:当最后一个绑定到Exchange上的队列删除后,自动删除该Exchange
Internal:当前Exchange是否用于RabbitMQ内部使用,默认为false。
Arguments:扩展参数,用于扩展AMQP协议定制使用
————————————————
版权声明:本文为CSDN博主「爱嘤斯塔」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_60389087/article/details/123140651
消息传输的模式有哪些?
1.简单模式
简单模式是最简单的消息模式,它包含一个生产者、一个消费者和一个队列。生产者向队列里发送消息,消费者从队列中获取消息并消费。
2.工作模式
工作模式是指向多个互相竞争的消费者发送消息的模式,它包含一个生产者、两个消费者和一个队列。两个消费者同时绑定到一个队列上去,当消费者获取消息处理耗时任务时,空闲的消费者从队列中获取并消费消息。
3.发布/订阅模式
发布/订阅模式是指同时向多个消费者消费消息的模式(类似广播的形式),它包含一个生产者、两个消费者、两个队列和一个交换机。两个消费者同时绑定到不同的队列上去,两个队列绑定到交换机上去,生产者通过发送消息到交换机,所有消费者接收并消费消息。
4.路由模式
路由模式是可以根据路由键选择性给多个消费者发送消息的模式,它包含一个生产者、两个消费者、两个队列和一个交换机。两个消费者同时绑定到不同的队列上去,两个队列通过绑定键绑定到交换机上去,生产者发送消息到交换机,交换机通过路由键转发到不同队列,队列绑定的消费者接收并消费消息
(要求路由键和绑定键相同)
常用的交换器主要分为一下三种:
fanout:如果交换器收到消息,将会广播到所有绑定的队列上
direct:如果路由键完全匹配,消息就被投递到相应的队列
topic:可以使来自不同源头的消息能够到达同一个队列。 使用 topic 交换器时,可以使用通配符
5. 通配符模式:就是绑定键是某种泛泛的符号规则,如果传过来的消息路由键与绑定键匹配,就能传递到对应的消息队列上
特殊匹配符号
*:只能匹配一个单词;
#:可以匹配零个或多个单词。
模式示意图
消息分发策略有哪些?
1、轮询分发
RabbitMQ 分发消息默认采用的轮训分发,但是在某种场景下这种策略并不是很好,当有两个消费者在处理任务时,其中有个消费者 处理任务的速度非常快,而另外一个消费者处理速度却很慢,这个时候我们还是采用轮训分发就会导致这处理速度快的这个消费者很大一部分时间处于空闲状态。我们可以通过修改消息分发德默认机制,来达到优化目的;
2、不公平分发
通过设置参数 channel.basicQos(1);实现不公平分发策略使得能者多劳;
通过RabbitMq的Web管理页面,可以看到Channels的Prefetch count属性显示为1则表示不公平分发成功;
上面介绍了basicQos,如果我们将qos的值设为1,那么你想一想会出现什么情况呢?信道中只允许传输一条消息,那么当这条消息处理完后,队列会立马发送下一条消息,所以这个时候快的不断处理,慢的等待当前处理完再处理下一条。这样就实现了能者多劳。
3、预值分发
当消息被消费者接收后,但是没有确认,此时这里就存在一个未确认的消息缓冲区,用于存储非被确认的消息,该缓存区的大小是没有限制的。
预取值: 定义通道上允许的未确认消息的最大数量。一旦未确认消息数量达到配置的最大数量,RabbitMQ 将停止在通道上传递更多消息,除非至少有一个未处理的消息被确认;例如,假设在通道上有未确认的消息 5、6、7,8,并且通道的预取值计数设置为 4,此时 RabbitMQ 将不会在该通道上再传递任何消息,除非至少有一个未应答的消息被 ack;例如, tag=6 这个消息刚刚被确认 ACK,此时RabbitMQ 将会感知这个情况到并再发送一条消息。
如果消费者消费了大量的消息但是没有确认的话,就会导致消费者连接节点的内存消耗变大,所以找到合适的预取值是一个反复试验的过程,不同的负载该值取值也不同 100 到 300 范 围内的值通常可提供最佳的吞吐量。
其实预取值设置就是一个非公平分发策略,我们通过设置qos来设置消息缓冲区允许未确认消息的最大数量,当我们设置为1时,是最保守的,吞吐量也是最低的,而100到300这个范围通常是最佳的.
————————————————
版权声明:本文为CSDN博主「一恍过去」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhuocailing3390/article/details/122462590
rabbitmq消息处理流程,即生产者生产消息,消费者接收消息过程
生产者消息投递过程
生产者连接到Broker 建立一个连接,然后开启一个信道
接着生产者声明一个交换器 ,并设置相关属性,比如交换机类型、是否持久化、是否自动删除、是否内置等
生产者声明一个队列井设置相关属性,比如是否排他、是否持久化、是否自动删除、消息最大过期时间、消息最大长度、消息最大字节数等
生产者通过绑定键将交换器和队列绑定起来
生产者发送消息至Broker ,发送的消息包含消息体和路由规则(可以理解为路由键)、交换器、优先级、是否持久化、过期时间、延时时间等信息的标签
相应的交换器根据接收到的路由键和与队列的绑定键查找相匹配的队列如果找到 ,则将从生产者发送过来的消息存入相应的队列中
如果没有找到 ,则根据生产者配置的属性选择丢弃还是回退给生产者
关闭信道
关闭连接
消费者消费消息过程
消费者连接到Broker ,建立一个连接,开启一个信道
消费者向 RabbitMQ Broker 请求消费相应队列中的消息,在这个过程中可能会设置消费者标签、是否自动确认、是否排他等
等待 RabbitMQ Broker 回应并投递相应队列中的消息, 消费者接收消息。
消费者确认接收到的消息
RabbitMQ从队列中删除相应己经被确认的消息
关闭信道
关闭连接。
涉及名词解释
在上方的消息流转过程中涉及了以下几个名词
是否持久化
将数据持久化到磁盘中
是否自动删除
当一个队列或交换机的所有消费者都与之断开连接时则这个队列或交换机就会自动删除
是否内置
客户端程序无法直接发送消息到这个交换器中,只能通过交换器路由到交换器这种方式
是否排他
如果一个队列被声明为排他队列,该队列仅对首次声明它的连接可见,并在连接断开时自动删除。这里需要注意的是:
排他队列是基于连接可见的,同一个连接的不同信道是可以同时访问同一连接创建的排他队列; "首次"是指如果一个连接己经声明了排他队列,其他连接是不允许建立同名的排他队列的,这个与普通队列不同:即使该队列是持久化的,一旦连接关闭或者客户端退出,该排他队列都会被自动删除,这种队列适用于一个客户端同时发送和读取消息的应用场景。
自动确认
消费者在订阅队列时,可以指定 autoAck 参数,当 autoAck 等于 false时, RabbitMQ会等待消费者显式地回复确认信号后才从内存(或者磁盘)中移去消息(实质上是先打上删除标记,之后再删除)
当 autoAck 等于 true 时, RabbitMQ 会自动把发送出去的消息置为确认,然后从内存(或者磁盘)中删除,而不管消费者是否真正地消费到了这些消息
采用消息确认机制后,只要设置 autoAck 参数为 false ,消费者就有足够的时间处理消息,不用担心处理消息过程中消费者进程挂掉后消息丢失的问题。因为 RabbitMQ 会一直等待持有消息直到消费者显式确认收到消息
————————————————
版权声明:本文为CSDN博主「javaxuexilu」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/javaxuexilu/article/details/100738621
如何保证消息的顺序消费
消息在投入到queue的时候是有顺序,如果只是单个消费者来处理对应的单个queue,是不会出现消息错乱的问题。但是在消费的时候有可能多个消费者消费同一个queue,由于各个消费者处理消息的时间不同,导致消息未能按照预期的顺序处理。其实根本的问题就是如何保证消息按照预期的顺序处理完成。
出现消费顺序错乱的情况
为了提高处理效率,一个queue存在多个consumer
一个queue只存在一个consumer,但是为了提高处理效率,consumer中使用了多线程进行处理
保证消息顺序性的方法
将原来的一个queue拆分成多个queue,每个queue都有一个自己的consumer。该种方案的核心是生产者在投递消息的时候根据业务数据关键值(例如订单ID哈希值对订单队列数取模)来将需要保证先后顺序的同一类数据(同一个订单的数据) 发送到同一个queue当中。
一个queue就一个consumer,在consumer中维护多个内存队列,根据业务数据关键值(例如订单ID哈希值对内存队列数取模)将消息加入到不同的内存队列中,然后多个真正负责处理消息的线程去各自对应的内存队列当中获取消息进行消费。
RabbitMQ保证消息顺序性总结:
核心思路就是根据业务数据关键值划分成多个消息集合,而且每个消息集合中的消息数据都是有序的,每个消息集合有自己独立的一个consumer。多个消息集合的存在保证了消息消费的效率,每个有序的消息集合对应单个的consumer也保证了消息消费时的有序性。
————————————————
版权声明:本文为CSDN博主「vnbear」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zw791029369/article/details/109561457
消息队列如何保证消息的可靠性传输
消息的可靠性传输分为两个问题,一个是保证消息不被重复消费,另一个是保证消息不丢失.
保证消息不重复被消费,就是保证消息的幂等性问题,消息的幂等性是指一个操作执行任意多次所产生的影响均与一次执行的影响相同,在mq里,也就是消息只能被消费一次,不能被重复消费.
来看看消息丢失的场景:
发送方丢失,可能发送方在发送消息的过程中,出现网络问题等导致mq接收不到消息,导致了消息丢失.
要解决这个问题,首先可以采用事务机制,在发送消息的时候实现事务机制,若是出现发送失败的情况,可以进行回滚,而让消息重新被发送.但是开启了事务,发送方就必须同步等待事务执行完毕或者回滚,导致消息一多,性能会下降.
但是,还有一个更好的办法:可以采用确认机制,发送方在发送消息的时候必须要保证要收到一个确认消息,如果没有收到或者收到失败的确认消息,就说明消息发送失败,要重新进行发送,确认机制是可以采用异步进行的,这样就极大地保证了在保留效率的基础上又能保证消息的不丢失问题.
第二个丢失问题可能是在mq方发生的,如果mq没有进行持久化,出现了宕机关机等情况,消息就会丢失,解决办法无非就是将消息进行持久化,这样在出现问题的时候可以及时对消息进行恢复.
第三个丢失问题可能在消费方发生,这和发送方丢失问题类似,解决这个问题也是采用确认机制,这样一来就可以实现效率上的保证和消息不丢失的保证.
但是解决了这些问题,就会产生下面的幂等性问题:
我们都知道mq是可以进行重发的,且只有在它认为失败的情况会进行重发.什么时候mq会认为它发送给消费者的消息是失败的呢?也就是超出了它等待消费者响应的时间,这是一个超时时间,若是过了这个时间消费者仍然没有响应,说明mq发送失败,就会进行重试,而其实这个时候消费者可能是没有失败的,它只是因为某个原因导致消费超出了mq的等待时间而已,这个时候mq再发送一次消息,消费者就会重复消费.
实现幂等性消费:
① 通过数据库:比如处理订单时,记录订单ID,在消费前,去数据库中进行查询该记录是否存在,如果存在则直接返回。
② 使用全局唯一ID,再配合第三组主键做消费记录,比如使用 redis 的 set 结构,生产者发送消息时给消息分配一个全局ID,在每次消费者开始消费前,先去redis中查询有没有消费记录,如果消费过则不进行处理,如果没消费过,则进行处理,消费完之后,就将这个ID以k-v的形式存入redis中(过期时间根据具体情况设置)。
————————————————
版权声明:本引用为CSDN博主「张维鹏」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/a745233700/article/details/115060109
如何处理消息堆积情况?
场景题:几千万条数据在MQ里积压了七八个小时。
9.1、出现该问题的原因:
消息堆积往往是生产者的生产速度与消费者的消费速度不匹配导致的。有可能就是消费者消费能力弱,渐渐地消息就积压了,也有可能是因为消息消费失败反复复重试造成的,也有可能是消费端出了问题,导致不消费了或者消费极其慢。比如,消费端每次消费之后要写mysql,结果mysql挂了,消费端hang住了不动了,或者消费者本地依赖的一个东西挂了,导致消费者挂了。
所以如果是 bug 则处理 bug;如果是因为本身消费能力较弱,则优化消费逻辑,比如优化前是一条一条消息消费处理的,那么就可以批量处理进行优化。
9.2、临时扩容,快速处理积压的消息:
(1)先修复 consumer 的问题,确保其恢复消费速度,然后将现有的 consumer 都停掉;
(2)临时创建原先 N 倍数量的 queue ,然后写一个临时分发数据的消费者程序,将该程序部署上去消费队列中积压的数据,消费之后不做任何耗时处理,直接均匀轮询写入临时建立好的 N 倍数量的 queue 中;
(3)接着,临时征用 N 倍的机器来部署 consumer,每个 consumer 消费一个临时 queue 的数据
(4)等快速消费完积压数据之后,恢复原先部署架构 ,重新用原先的 consumer 机器消费消息。
这种做法相当于临时将 queue 资源和 consumer 资源扩大 N 倍,以正常 N 倍速度消费。
9.4、MQ长时间未处理导致MQ写满的情况如何处理:
如果消息积压在MQ里,并且长时间都没处理掉,导致MQ都快写满了,这种情况肯定是临时扩容方案执行太慢,这种时候只好采用 “丢弃+批量重导” 的方式来解决了。首先,临时写个程序,连接到mq里面消费数据,消费一个丢弃一个,快速消费掉积压的消息,降低MQ的压力,然后在流量低峰期时去手动查询重导丢失的这部分数据
9.5 短时间内无法扩容或者扩容无法完全解决问题
可以尝试降低一些非核心业务的消息处理,其次可以通过监控排查,优化消费者端的业务代码或者查看是否存在一些消息被重复消息的情况.
————————————————
版权声明:本文为CSDN博主「张维鹏」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/a745233700/article/details/115060109
如何保证消息队列的高可用?
RabbitMQ 是基于主从(非分布式)做高可用性的,RabbitMQ 有三种模式:单机模式、普通集群模式、镜像集群模式
10.1、单机模式:一般没人生产用单机模式
10.2、普通集群模式:
普通集群模式用于提高系统的吞吐量,通过添加节点来线性扩展消息队列的吞吐量。也就是在多台机器上启动多个 RabbitMQ 实例,而队列 queue 的消息只会存放在其中一个 RabbitMQ 实例上,但是每个实例都同步 queue 的元数据(元数据是 queue 的一些配置信息,通过元数据,可以找到 queue 所在实例)。消费的时候,如果连接到了另外的实例,那么该实例就会从数据实际所在的实例上的queue拉取消息过来,就是说让集群中多个节点来服务某个 queue 的读写操作
但普通集群模式的缺点在于:无高可用性,queue所在的节点宕机了,其他实例就无法从那个实例拉取数据;RabbitMQ 内部也会产生大量的数据传输。
10.3、镜像队列集群模式:
镜像队列集群是RabbitMQ 真正的高可用模式,集群中一般会包含一个主节点master和若干个从节点slave,如果master由于某种原因失效,那么按照slave加入的时间排序,"资历最老"的slave会被提升为新的master。
镜像队列下,所有的消息只会向master发送,再由master将命令的执行结果广播给slave,所以master与slave节点的状态是相同的。比如,每次写消息到 queue 时,master会自动将消息同步到各个slave实例的queue;如果消费者与slave建立连接并进行订阅消费,其实质上也是从master上获取消息,只不过看似是从slave上消费而已,比如消费者与slave建立了TCP连接并执行Basic.Get的操作,那么也是由slave将Basic.Get请求发往master,再由master准备好数据返回给slave,最后由slave投递给消费者。
从上面可以看出,队列的元数据和消息会存在于多个实例上,也就是说每个 RabbitMQ 节点都有这个 queue 的完整镜像,任何一个机器宕机了,其它机器节点还包含了这个 queue 的完整数据,其他消费者都可以到其它节点上去消费数据。
(1)缺点:
① 性能开销大,消息需要同步到所有机器上,导致网络带宽压力和消耗很重
② 非分布式,没有扩展性,如果 queue 的数据量大到这个机器上的容量无法容纳了,此时该方案就会出现问题了
(2)如何开启镜像集群模式呢?
在RabbitMQ 的管理控制台Admin页面下,新增一个镜像集群模式的策略,指定的时候是可以要求数据同步到所有节点的,也可以要求同步到指定数量的节点,再次创建 queue 的时候,应用这个策略,就会自动将数据同步到其他的节点上去了。
————————————————
版权声明:本文为CSDN博主「张维鹏」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/a745233700/article/details/115060109
说说你对rabbitmq持久化机制的理解
RabbitMQ 的消息默认存放在内存上面,如果不特别声明设置,消息不会持久化保存到硬盘上面的,如果节点重启或者意外crash掉,消息就会丢失。所以就要对消息进行持久化处理。
rabbitmq的持久化分为队列持久化、消息持久化和交换器持久化。
1)队列的持久化是在定义队列时的durable参数来决定的,当durable为true时,才代表队列会持久化。
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
//第二个餐胡设置为true,代表队列持久化
channel.queueDeclare("queue.persistent.name", true, false, false, null);
- 如果要在重启后保持消息的持久化必须设置消息是持久化的标识。
//通过传入MessageProperties.PERSISTENT_PLAIN就可以实现消息持久化
channel.basicPublish("exchange.persistent", "persistent", MessageProperties.PERSISTENT_TEXT_PLAIN, "persistent_test_message".getBytes());
- 上面阐述了队列的持久化和消息的持久化,如果不设置exchange的持久化对消息的可靠性来说没有什么影响,**但是同样如果exchange不设置持久化,那么当broker服务重启之后,exchange将不复存在,那么既而发送方rabbitmq producer就无法正常发送消息。**这里建议,同样设置exchange的持久化。exchange的持久化设置也特别简单.
//durable为true则开启持久化
Exchange.DeclareOk exchangeDeclare(String exchange,String type,boolean durable)throws IOException
所以一般三个持久化设置都要进行.
可以对所有消息都持久化吗?
不可以
持久化的操作是将数据写入磁盘中,效率上肯定比写入内存中要慢很多倍.而我们一般用mq会处理很多业务消息,若是所有消息都持久化,压力无疑是巨大的.所以持久化策略需要综合考虑,以及可能遇到的问题和解决方案,或者我们可以让一些必要数据持久化.
说说你对rabbitmq应答机制的理解
一、消息应答机制
消费者完成一个任务可能需要一段时间,如果其中一个消费者处理一个长的任务并仅只完成了部分突然它挂掉,由于RabbitMQ 一旦向消费者传递了一条消息,便立即将该消息标记为删除。在这种情况下,突然有个消费者挂掉了,我们将丢失正在处理的消息,以及后续发送给该消费者的消息也无法接收到。
为了保证消息在发送过程中不丢失,rabbitmq 引入消息应答机制。
消费者在接收到消息并且处理该消息之后,告诉 rabbitmq 它已经处理了,rabbitmq 可以把该消息删除掉了。
二、自动应答
消息发送后立即被认为已经传送成功,这种模式需要在高吞吐量和数据传输安全性方面做权衡,因为这种模式如果消息在接收到之前,消费者出现连接或者 channel 关闭,那么消息就丢失了,当然另一方面这种模式消费者可以传递过载的消息,没有对传递的消息数量进行限制,当然这样有可能使得消费者由于接收太多还来不及处理的消息,导致这些消息的积压,最终使得内存耗尽,最终这些消费者线程被操作系统杀死,所以这种模式仅适用在消费者可以高效并以某种速率能够处理这些消息的情况下使用。
三、手动消息应答的方法
//用于肯定确认,RabbitMQ 已知道该消息并且成功的处理消息,可以将其丢弃了
channel.basicAck();
//用于否定确认
channel.basicNack();
//用于否定确认,不处理该消息了直接拒绝,可以将其丢弃了
channel.basicReject();
如果消费者由于某些原因失去连接(其通道已关闭,连接已关闭或 TCP 连接丢失),导致消息未发送 ACK 确认,RabbitMQ 将了解到消息未完全处理,并将对其重新排队。如果此时其他消费者可以处理,它将很快将其重新分发给另一个消费者。这样,即使某个消费者偶尔死亡,也可以确保不会丢失任何消息。
————————————————
版权声明:本文为CSDN博主「煎丶包」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_39794062/article/details/118112321
说一下Rabbitmq事务机制
1、概述
在使用RabbitMQ的时候,我们可以通过消息持久化操作来解决因为服务器的异常奔溃导致的消息丢失,除此之外我们还会遇到一个问题,当消息的发布者在将消息发送出去之后,消息到底有没有正确到达broker代理服务器呢?如果不进行特殊配置的话,默认情况下发布操作是不会返回任何信息给生产者的,也就是默认情况下我们的生产者是不知道消息有没有正确到达broker的,如果在消息到达broker之前已经丢失的话,持久化操作也解决不了这个问题,因为消息根本就
以上是关于Rabbitmq面试题总结,非常详细,杜绝标题党,不详细你打我,下次不写博客了的主要内容,如果未能解决你的问题,请参考以下文章