中间件面试专题:RabbitMQ高频面试问题

开篇介绍
大家好,公众号【Java极客思维】近期会整理一些Java高频面试题分享给小伙伴,也希望看到的小伙伴在找工作过程中能够用得到!本章节主要针对Java一些消息中间件高频面试题进行分享。
Q1:
RabbitMQ 的介绍、用途、好处?
RabbitMQ是一款开源的,Erlang编写的,基于AMQP协议的消息中间件。
作用:解耦 、 异步 、 削峰 。
优点:解耦、异步、削峰;
缺点:降低了系统的稳定性:系统中使用了消息队列,如果消息队列挂了,那么系统也会挂。降低了系统可用性。
加入消息队列,要考虑很多方面的问题,比如:一致性问题 、如何保证消息不被重复消费 、 如何保证消息可靠性传输 等。因此考虑的因素有很多方面,复杂性增加。
Q2:
RabbitMQ 包括哪些要素?
生产者 :消息的创建者,发送到RabbitMQ
消费者 :连接到RabbitMQ,订阅到队列上,消费消息,持续订阅(basicConsumer)和单条订阅(basicGet)
消息 :包含有效载荷和标签,有效载荷指要传输的数据,标签描述了有效载荷,并且RabbitMQ用它来决定谁获得消息,消费者只能拿到有效载荷,并不知道生产者是谁。
Q3:
RabbitMQ 什么是信道?
信道:是生产者、消费者与RabbitMQ通信的渠道,生产者publish或是消费者subscribe一个队列都是通过信道来通信的。信道是建立在TCP连接上的虚拟连接。就是说RabbitMQ在一条TCP上建立成百上千个信道来达到多个线程处理,这个TCP被多个线程共享,每个线程对应一个信道,信道在RabbitMQ都有一个唯一的ID,保证了信道私有性,对应上唯一的线程使用。
疑问:为什么不建立多个TCP连接?
原因是RabbitMQ需要保证性能,系统为每个线程开辟一个TCP是非常消耗性能的,美妙成百上千的建立销毁TCP会严重消耗系统性能;所以RabbitMQ选择建立多个信道(建立在TCP的虚拟连接)连接到RabbitMQ上
Q4:
RabbitMQ概念里的channel、exchange 和 queue是逻辑概念,还是对应着进程实体?作用分别是什么?
queue 具有自己的 erlang 进程;
exchange 内部实现为保存 binding 关系的查找表;
channel 是实际进行路由工作的实体,负责按照 routing_key 将 message投递给queue。
由 AMQP 协议描述可知,channel 是真实TCP连接之上的 虚拟连接 , 所有AMQP 命令都是通过 channel 发送的,且每一个 channel 有 唯一的ID 。一个 channel 只能被单独一个操作系统线程使用,所以投递到特定的 channel 上的 message 是有顺序的。单一个操作系统线程上允许使用多个channel。
Q5:
RabbitMQ消息是如何路由的?
消息路由必须有三部分:交换器、路由、绑定。
生产者把消息发布到交换器上,绑定决定了消息如何从路由器路由到特定的队列;消息最终到达队列,并被消费者接收。
消息发布到交换器时,消息将拥有一个 路由键(routing key) , 在消息创建时设定。
通过队列路由键,可以把队列绑定到交换器上。
消息到达交换器后,RabbitMQ会将消息的路由键与队列的路由键进行匹配(针对不同的交换器有不同的路由规则)。如果能够匹配到队列,则消息会投递到相应队列中;如果不能匹配到任何队列,消息将进入"黑洞"。
常用的交换器主要分为以下三种:
direct :如果路由键完全匹配,消息就会被投递到相应的队列;每个AMQP的实现都必须有一个direct交换器,包含一个空白字符串名称的默认交换器。声明一个队列时,会自动绑定到默认交换器,并且以队列名称作为路由键:channel -> basic_public($msg, '', 'queue-name')
fanout : 如果交换器收到消息,将会广播到所有绑定的队列上;
topic :可以使来自不同源头的消息能够到达同一个队列。使用topic交换器时,可以使用通配符,比如:"*" 匹配特定位置的任意文本,"." 把路由键分为了几个标识符, "#" 匹配所有规则等。
特别注意:发往topic交换器的消息不能随意的设置选择键(routing_key),必须是有"."隔开的一系列的标识符组成。
Q6:
RabbitMQ消息确认过程?
消费者收到的每一条消息都必须进行确认(自动确认和自行确认)
消费者在声明队列时,可以置顶autoAck参数,当autoAck = false时,RabbitMQ会等待消费者显式发送回 ack 信号后才从内存(和磁盘,如果是持久化消息的话)中删除消息,否则RabbitMQ会在队列中消息被消费后立即删除它。
采用消息确认机制后,只要使 autoAck = false,消费者就有足够的时间处理消息(任务),不用担心处理消息过程中消费者进程挂掉后消息丢失的问题,因为RabbitMQ会一直持有消息直到消费者显式调用basicAck为止。
当autoAck = false时,对于RabbitMQ服务器端而言,队列中的消息分成了两部分:一部分是等待投递给消费者的消息;一部分是已经投递给消费者,但是还没有收到消费者ack信号的消息。如果服务器端一直没有收到消费者的ack信号,并且消费此消息的消费者已经断开连接,则服务器端会安排该消息 重新进入队列,等待投递给下一个消费者(也可能还是原来的那个消费者)。
RabbitMQ不会为 ack消息设置超时时间,它判断此消息是否需要重新投递给消费者的唯一依据是消费该消息的消费者连接是否已经断开。这么设计的原因是RabbitMQ允许消费者消费一条消息的时间可以很久很久。
Q7:
如何保证RabbitMQ不被重复消费?
正常情况下,消费者在消费消息的时候,消费完毕后,会发送一个确认信息给消息队列,消息队列就知道该消息被消费了,就会将该消息从消息队列中删除。
但是因为网络传输等故障,确认信息没有传送到消息队列,导致消息队列不知道自己已经消费过该消息了,再次将消息分发给其他的消费者。
解决思路:
保证消息的唯一性,就算是多次传输,不要让消息的多次消费带来影响;
保证消息幂等性;
比如:在写入消息队列的数据做唯一标识,消费消息时,根据唯一标识判断该消息是否被消费过。
Q8:
如何保证RabbitMQ消息的可靠传输?
消息不可靠的情况可能是消息丢失,劫持等原因;
丢失可能又分为:
生产者丢失消息
消息队列丢失消息
消费者丢失消息
生产者丢失消息:
从生产者弄丢数据来看,RabbitMQ提供了 transaction 机制 和 confirm 模式 来确保生产者不丢失消息;
transaction机制: 发送消息前,开启事务(channel.exSelect()),然后发送消息,如果发送过程中出现异常,事务就会回滚(channel.txRollback()),如果发送成功则提交事务(channel.txCommit())。
confirm模式:一般这种模式居多,一旦channel进入confirm模式,所有在该信道上发布的消息都将会被指派一个唯一的ID(从1开始),一旦消息被投递到所有匹配的队列后;RabbitMQ就会发送一个ACK给生产者(包含消息的唯一ID),这就使得生产者知道消息已经正确到达目的队列了。
如果RabbitMQ没能处理该消息,则会发送一个Nack消息回来,这样可以进行重试操作。
消息队列丢失消息:
针对消息队列丢失数据的情况,一般是开启持久化磁盘的配置:
将队列的持久化标识 durable 设置为 true , 则代表是一个持久的队列,发送消息的时候讲 deliveryMode=2 这样设置以后,即使RabbitMQ挂了,重启后也能恢复数据。
消费者丢失消息:
消费者丢失消息一般是因为采用了自动确认消息模式,改为手动确认消息即可。
消费者在收到消息之后,处理消息之前,会自动回复RabbitMQ已收到消息;如果这时候处理消息失败,就会丢失该消息;
解决方案:处理消息成功后,手动回复确认消息。
点关注、不迷路
如果觉得文章不错,欢迎关注、点赞、收藏,你们的支持是我创作的动力,感谢大家。
如果文章写的有问题,请不要吝啬,欢迎留言指出,我会及时核查修改。
如果你还想更加深入的了解我,可以微信搜索「Java极客思维」进行关注。每天8:00准时推送技术文章,让你的上班路不在孤独,而且每月还有送书活动,助你提升硬实力!
中间件面试专题:RabbitMQ高频面试问题的更多相关文章
- 字节跳动上传了一份“面试官版Android面试小册”,不讲一句废话,全是精华
前言 金三银四马上就到了,很多粉丝朋友私信希望我出一篇面试专题或者分享面试相关的笔记来学习,这不今天就给大家安排上了?(都是干货,错过就是亏.) 下面的面试笔记都是精心整理好免费分享给大家的,希望新朋 ...
- 手撕面试官系列(八):分布式通讯ActiveMQ+RabbitMQ+Kafka面试专题
ActiveMQ专题 (面试题+答案领取方式见主页) 什么是 ActiveMQ? ActiveMQ 服务器宕机怎么办? 丢消息怎么办? 持久化消息非常慢. 消息的不均匀消费. 死信队列. Active ...
- php面试专题---MySQL分表
php面试专题---MySQL分表 一.总结 一句话总结: 分库分表要数据达到一定的量级才用,这样才有效率,不然利不一定大于弊,可能会增加一次I/O消耗 1.分库分表的使用量级是多少? 单表行数超过 ...
- php面试专题---MySQL分区
php面试专题---MySQL分区 一.总结 一句话总结: mysql的分区操作还比较简单,好处是也不用自己动手建表进行分区,和水平分表有点像 1.mysql分区简介? 一个表或索引-->N个物 ...
- 2019前端面试系列——JS高频手写代码题
实现 new 方法 /* * 1.创建一个空对象 * 2.链接到原型 * 3.绑定this值 * 4.返回新对象 */ // 第一种实现 function createNew() { let obj ...
- 手撕面试官系列(六):并发+Netty+JVM+Linux面试专题
并发面试专题 (面试题+答案领取方式见侧边栏) 现在有 T1.T2.T3 三个线程,你怎样保证 T2 在 T1 执行完后执行,T3 在 T2 执行完后执行? 在 Java 中 Lock 接口比 syn ...
- php面试专题---22、网站优化 总结
php面试专题---22.网站优化 总结 一.总结 一句话总结: 主要从前端.后端.数据库.资源四个方面开始发散 前端浏览器缓存和数据压缩前端优化(减少HTTP请求次数) 资源流量优化(防盗链处理)C ...
- php面试专题---21、MVC框架基本工作原理考察点
php面试专题---21.MVC框架基本工作原理考察点 一.总结 一句话总结: 会的东西快速过,不要浪费时间,生命有限,都是一些很简单的东西. 1.mvc框架单一入口的 优势 是什么? 可以进行统一的 ...
- php面试专题---20、MySQL的安全性考点
php面试专题---20.MySQL的安全性考点 一.总结 一句话总结: 还是得多看视频,教程看的浮光掠影,容易get不到重点:比如预处理防sql注入之前是挺熟,后面就忘记了,而且看文章get不到点 ...
随机推荐
- [MIT6.006] 10. Open Addressing, Cryptographic Hashing 开放定址,加密哈希
前几节课讲散列表的时候,我们需要用Chaining,链接法需要用到指针pointer,但有一种方法可以不要Chaining和指针,还能在发生冲突时,为产生冲突的关键字寻找下一个"空" ...
- binary hacks读数笔记(nm命令)
nm命令(names):输出包含三个部分:1 符号值.默认显示十六进制,也可以指定: 2 符号类型.小写表示是本地符号,大写表示全局符号(external); 3 符号名称. 例如:nm Simple ...
- GDT,LDT,GDTR,LDTR (转 侵删)
一.引入 保护模式下的段寄存器 由 16位的选择器 与 64位的段描述符寄存器 构成 段描述符寄存器: 存储段描述符 选择器:存储段描述符的索引 段寄存器(16位选择子,64为隐藏信息) 原先实模式下 ...
- Linux操作系统选择
主流的操作系统 ubuntu centos debian oracle linux 主要使用的操作系统就是上面几个,主要是ubuntu和centos,debian是基于ubuntu改的,oracle ...
- if __name__ == "__main__"的疑惑
Python中if __name__ == "__main__"详细解释: 想必很多初次接触python都会见到这样一个语句,if __name__ == "__main ...
- 「NOIP2016」天天爱跑步 题解
(声明:图片来源于网络) 「NOIP2016」天天爱跑步 题解 题目TP门 题目 题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是 ...
- Linux(CentOS6.8)配置ActiveMQ
1.下载ActiveMQ http://activemq.apache.org/ 注:若是想下载老版本的ActiveMQ可以通过以下链接下载 http://activemq.apache.org/do ...
- php socket 编程问题总结
1. PHP declare(ticks=N); 的作用 https://blog.csdn.net/udefined/article/details/24333333 2. pack &&a ...
- python-基础入门-7基础
1.语法和语句 Python中有一些基本规则和特殊字符 1)#符号之后的表示注释 2)\n符号表示换行 3)\继续上一行的内容 推荐使用括号,这样可读性更好 4):将两个语句链接在一行中 类似于c语言 ...
- bWAPP----OS Command Injection
OS Command Injection 界面: 给一个域名,它帮你返回DNS 代码: 1 <div id="main"> 2 3 <h1>OS Comma ...