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

开篇介绍面试

你们好,公众号【Java极客思惟】近期会整理一些Java高频面试题分享给小伙伴,也但愿看到的小伙伴在找工做过程当中可以用获得!本章节主要针对Java一些消息中间件高频面试题进行分享。服务器

通知:公众号【Java极客思惟】正在送书福利活动,关注公众号并参加福利活动吧!只有参与了本次活动的小伙伴才可以参与年末的大福利,不要错过呀~微信

Q1:网络

RabbitMQ 的介绍、用途、好处?异步

RabbitMQ是一款开源的,Erlang编写的,基于AMQP协议的消息中间件。性能

做用:解耦 、 异步 、 削峰 。spa

优势:解耦、异步、削峰;操作系统

缺点:下降了系统的稳定性:系统中使用了消息队列,若是消息队列挂了,那么系统也会挂。下降了系统可用性。线程

加入消息队列,要考虑不少方面的问题,好比:一致性问题 、如何保证消息不被重复消费 、 如何保证消息可靠性传输 等。所以考虑的因素有不少方面,复杂性增长。设计

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准时推送技术文章,让你的上班路不在孤独,并且每个月还有送书活动,助你提高硬实力!

相关文章
相关标签/搜索