栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 前沿技术 > 大数据 > 大数据系统

RabbitMQ面试题

RabbitMQ面试题

目录
  • vhost 是什么?起什么作⽤?
  • RabbitMQ 有⼏种⼴播类型?
  • 要保证消息持久化成功的条件有哪些?
  • 如何确保消息正确地发送⾄RabbitMQ?如何确保消息接收⽅消费了消息?
  • RabbitMQ有哪些重要的⻆⾊?
  • RabbitMQ的使⽤场景有哪些?
  • RabbitMQ 怎么避免消息丢失?
  • RabbitMQ 的消息是怎么发送的?
  • 若cluster中拥有某个queue的owner node失效了,且该queue 被声明具有 durable属性,是否能够成功从其他node上重新声明该 queue ?
  • 客户端连接到cluster中的任意node上是否都能正常⼯作?
  • RabbitMQ有什么优缺点?
  • 什么是RabbitMQ?为什么使⽤RabbitMQ?
  • 死信队列和延迟队列的使⽤
  • 如何避免消息重复投递或重复消费?
  • 消息怎么路由?
  • 消息如何分发?
  • 消息基于什么传输?

vhost 是什么?起什么作⽤?

至少记住这句话:虚拟 broker,有独⽴的权限系统,做到 vhost 范围的用户控制,作为不同权限隔离的⼿段。

vhost 可以理解为虚拟 broker ,即 mini-RabbitMQ server。其内部均含有独⽴的 queue、 exchange和 binding 等,但最最重要的是,其拥有独⽴的权限系统,可以做到 vhost 范围的 ⽤户控制。当然,从RabbitMQ 的全局⻆度,vhost 可以作为不同权限隔离的⼿段(⼀个典型 的例⼦就是不同的应⽤可以跑在不同的 vhost 中)。

RabbitMQ 有⼏种⼴播类型?

至少记住这句话:三种:扇出,直达,主题。

三种⼴播模式:
①fanout:所有bind到此exchange的queue都可以接收消息(纯⼴播,绑定到RabbitMQ的接 受者都能收到消息);
②direct:通过routingKey和exchange决定的那个唯⼀的queue可以接收消息; ③topic:所有符合routingKey(此时可以是⼀个表达式)的routingKey所bind的queue可以接 收消息;

要保证消息持久化成功的条件有哪些?

至少记住这句话:队列声明时设置持久化,消息设置为持久化,消息到达交换器和队列。

①声明队列必须设置持久化durable设置为 true。
②消息推送投递模式必须设置持久化,deliveryMode设置为2(持久)。
③消息已经到达持久化交换器。
④消息已经到达持久化队列。
以上四个条件都满⾜才能保证消息持久化成功。

如何确保消息正确地发送⾄RabbitMQ?如何确保消息接收⽅消费了消息?

至少记住这句话:发送方开确认模式,消息投递到队列或者持久化就发回确认,丢失也发回未确认,发送方可单个,批量和异步确认发布。接收方
接收方接收每条消息后需确认,只要连接没断一直等。

1、发送⽅确认模式
①将信道设置成/confirm/i模式(发送⽅确认模式),则所有在信道上发布的消息都会被指派⼀个 唯⼀的ID。
②⼀旦消息被投递到⽬的队列后,或者消息被写⼊磁盘后(可持久化的消息),信道会发送⼀个 确认给⽣产者(包含消息唯⼀ ID)。
③如果RabbitMQ发⽣内部错误从⽽导致消息丢失,会发送⼀条 nack(notacknowledged, 未确认)消息。
④发送⽅确认模式是异步的,⽣产者应⽤程序在等待确认的同时,可以继续发送消息。当确认 消息到达⽣产者应⽤程序,⽣产者应⽤程序的回调⽅法就会被触发来处理确认消息。
2、接收⽅确认机制
①消费者接收每⼀条消息后都必须进⾏确认(消息接收和消息确认是两个不同操作)。只有消费 者确认了消息,RabbitMQ 才能安全地把消息从队列中删除。
②这⾥并没有⽤到超时机制,RabbitMQ仅通过Consumer的连接中断来确认是否需要重新发 送消息。也就是说,只要连接不中断,RabbitMQ给了Consumer⾜够⻓的时间来处理消息。 保证数据的最终⼀
致性。
3、下⾯罗列⼏种特殊情况
①如果消费者接收到消息,在确认之前断开了连接或取消订阅,RabbitMQ会认为消息没有被 分发,然后重新分发给下⼀个订阅的消费者。(可能存在消息重复消费的隐患,需要去重)
②如果消费者接收到消息却没有确认消息,连接也未断开,则RabbitMQ认为该消费者繁忙, 将不会给该消费者分发更多的消息。

RabbitMQ有哪些重要的⻆⾊?

至少记住这句话:生产者,消费者,代理。

RabbitMQ中重要的⻆⾊有:⽣产者、消费者和代理:
①⽣产者:消息的创建者,负责创建和推送数据到消息服务器;
②消费者:消息的接收⽅,⽤于处理数据和确认消息;
③代理:就是RabbitMQ本身,⽤于扮演“快递”的⻆⾊,本身不⽣产消息,只是扮演“快递”的 ⻆⾊。

RabbitMQ的使⽤场景有哪些?

至少记住这句话:异步交互的地⽅,比如打电话同时发短信。多个应⽤之间的耦合,解耦。应⽤内的同步变异步,⽐如订单处理流量削峰。发布订阅。

①跨系统的异步通信,所有需要异步交互的地⽅都可以使⽤消息队列。就像我们除了打电话(同 步)以外,还需要发短信,发电⼦邮件(异步)的通讯⽅式。
②多个应⽤之间的耦合,由于消息是平台⽆关和语⾔⽆关的,⽽且语义上也不再是函数调⽤, 因此更适合作为多个应⽤之间的松耦合的接⼝。基于消息队列的耦合,不需要发送⽅和接收⽅ 同时在线。在企业应⽤集成(EAI)中,⽂件传输,共享数据库,消息队列,远程过程调⽤都可 以作为集成的⽅法。
③应⽤内的同步变异步,⽐如订单处理,就可以由前端应⽤将订单信息放到队列,后端应⽤从 队列⾥依次获得消息处理,⾼峰时的⼤量订单可以积压在队列⾥慢慢处理掉。由于同步通常意 味着阻塞,⽽⼤量线程的阻塞会降低计算机的性能。
④消息驱动的架构(EDA),系统分解为消息队列,和消息制造者和消息消费者,⼀个处理流程 可以根据需要拆成多个阶段(Stage),阶段之间⽤队列连接起来,前⼀个阶段处理的结果放⼊ 队列,后⼀个阶段从队列中获取消息继续处理。
⑤应⽤需要更灵活的耦合⽅式,如发布订阅,⽐如可以指定路由规则。
⑥跨局域⽹,甚⾄跨城市的通讯(CDN⾏业),⽐如北京机房与⼴州机房的应⽤程序的通信。

RabbitMQ 怎么避免消息丢失?

至少记住这些:
①消息持久化;
②ACK确认机制;
③设置集群镜像模式;
④消息补偿机制。

  • 那么RabbitMQ消费者的补偿原理是怎样的呢?
    @RabbitListener 底层使用了AOP进行拦截,如果程序没有抛异常,自动提交事务。如果AOP使用异常通知拦截获取异常信息的话,自动实现补偿机制,该消息会缓存到RabbitMQ服务端进行缓存,一直重试到不抛异常为准。

  • 如何合理选择重试机制
    下面有两种情况:
    情况1: 消费者获取到消息后,调用第三方接口,但接口暂时无法访问,是否需要重试? (需要重试机制)
    情况2: 消费者获取到消息后,抛出数据转换异常,是否需要重试?(不需要重试机制)。
    那么该如何解决呢?

可以采用“日志记录”+“定时任务健康检查”+“人工补偿”来解决。比如:出现了错误,可以日志记录下来,定时任务去定时查日志,人工进行补偿。

RabbitMQ 的消息是怎么发送的?

至少记住这句话:客户端和mq建立tcp真实连接,在此上建立带有唯一id的channel虚拟连接,操作都是通过这个channel来完成。

⾸先客户端必须连接到 RabbitMQ 服务器才能发布和消费消息,客户端和 rabbit server 之间 会创建⼀个 tcp 连接,⼀旦 tcp 打开并通过了认证(认证就是你发送给 rabbit 服务器的⽤户名 和密码),你的客户端和 RabbitMQ 就创建了⼀条 amqp 信道(channel),信道是创建在 “真实” tcp 上的虚拟连接,amqp 命令都是通过信道发送出去的,每个信道都会有⼀个唯⼀的 id,不论是发布消息,订阅队列都是通过这个信道完成的。

若cluster中拥有某个queue的owner node失效了,且该queue 被声明具有 durable属性,是否能够成功从其他node上重新声明该 queue ?

不能,在这种情况下,将得到404 NOT_FOUND错误。只能等queue所 属的node恢复后才能 使⽤该queue。但若该queue本身不具有durable 属性,则可在其他node上重新声明。

客户端连接到cluster中的任意node上是否都能正常⼯作?

是的。客户端感觉不到有何不同。

RabbitMQ有什么优缺点?

优点:解耦、异步、削峰;
缺点:降低了系统的稳定性:本来系统运⾏好好的,现在你⾮要加⼊个消息队列进去,那消息 队列挂了,你的系统不是呵呵了。因此,系统可⽤性会降低;增加了系统的复杂性:加⼊了消息队列,要多考虑很多⽅⾯的问题,⽐如:⼀致性问题、如何 保证消息不被重复消费、如何保证消息可靠性传输等。因此,需要考虑的东⻄更多,复杂性增 ⼤。

什么是RabbitMQ?为什么使⽤RabbitMQ?

RabbitMQ是⼀款开源的,Erlang编写的,基于AMQP协议的,消息中间件;
可以⽤它来:解耦、异步、削峰。

死信队列和延迟队列的使⽤

死信消息:
消息被拒绝(Basic.Reject或Basic.Nack)并且设置 requeue 参数的值为 false 消息过期了 队列达到最⼤的⻓度 过期消息:

在 rabbitmq 中存在2种⽅可设置消息的过期时间,第⼀种通过对队列进⾏设置,这种设置 后,该队列中所有的消息都存在相同的过期时间,第⼆种通过对消息本身进⾏设置,那么每条 消息的过期时间都不⼀样。如果同时使⽤这2种⽅法,那么以过期时间⼩的那个数值为准。当 消息达到过期时间还没有被消费,那么那个消息就成为了⼀个 死信 消息。

队列设置:在队列申明的时候使⽤ x-message-ttl 参数,单位为 毫秒

单个消息设置:是设置消息属性的 expiration 参数的值,单位为 毫秒

延时队列:在rabbitmq中不存在延时队列,但是我们可以通过设置消息的过期时间和死信队 列来模拟出延时队列。消费者监听死信交换器绑定的队列,⽽不要监听消息发送的队列。
有了以上的基础知识,我们完成以下需求:

需求:⽤户在系统中创建⼀个订单,如果超过时间⽤户没有进⾏⽀付,那么⾃动取消订单。

分析:

1、上⾯这个情况,我们就适合使⽤延时队列来实现,那么延时队列如何创建
2、延时队列可以由 过期消息+死信队列 来设置
3、过期消息通过队列中设置 x-message-ttl 参数实现
4、死信队列通过在队列申明时,给队列设置 x-dead-letter-exchange 参数,然后另外申明 ⼀个队列绑定x-dead-letter-exchange对应的交换器。

ConnectionFactory factory = new ConnectionFactory();
factory.setHost("127.0.0.1");
factory.setPort(AMQP.PROTOCOL.PORT);
factory.setUsername("guest");
factory.setPassword("guest");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
// 声明⼀个接收被删除的消息的交换机和队列 
String EXCHANGE_DEAD_NAME ="exchange.dead"; 
String QUEUE_DEAD_NAME = "queue_dead";
channel.exchangeDeclare(EXCHANGE_DEAD_NAME, BuiltinExchangeType.DIRECT);
channel.queueDeclare(QUEUE_DEAD_NAME, false, false, false, null);
channel.queueBind(QUEUE_DEAD_NAME, EXCHANGE_DEAD_NAME, "routingkey.dead");
String EXCHANGE_NAME = "exchange.fanout";
String QUEUE_NAME = "queue_name";
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
Map arguments = new HashMap();
// 统⼀设置队列中的所有消息的过期时间 
arguments.put("x-message-ttl", 30000); 
// 设置超过多少毫秒没有消费者来访问队列,就删除队列的时间 
arguments.put("x-expires",20000); 
// 设置队列的最新的N条消息,如果超过N条,前⾯的消息将从队列中移除掉
arguments.put("x-max-length", 4); 
// 设置队列的内容的最⼤空间,超过该阈值就删除之前的消息 
arguments.put("x-max-length-bytes", 1024); 
// 将删除的消息推送到指定的交换机,⼀般x-dead-letter-exchange和x-dead-letterrouting-key需要同时设置
arguments.put("x-dead-letter-exchange", "exchange.dead"); 
// 将删除的消息推送到指定的交换机对应的路由键 
arguments.put("x-dead-letter-routing-key","routingkey.dead"); 
// 设置消息的优先级,优先级⼤的优先被消费 
arguments.put("x-maxpriority", 10); 
channel.queueDeclare(QUEUE_NAME, false, false, false,
arguments); 
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ""); 
String message= "Hello RabbitMQ: ";
for (int i = 1; i <= 5; i++) {
 // expiration: 设置单条消息的过期时间 
 AMQP.BasicProperties.Builder properties= new AMQP.BasicProperties().builder() .priority(i).expiration( i * 1000 +"");
 channel.basicPublish(EXCHANGE_NAME, "", properties.build(), (message + i).getBytes("UTF-8"));
}
channel.close();
connection.close();
如何避免消息重复投递或重复消费?

至少记住这句话:有去重的id作为幂等依据,数据库里面给消息唯一主键就能去重,redis本身就幂等,大招可以第三方介质redis全局记录id和消息映射。

在消息⽣产时,MQ内部针对每条⽣产者发送的消息⽣成⼀个inner-msg-id,作为去重和幂 等的依据(消息投递失败并重传),避免重复的消息进⼊队列;在消息消费时,要求消息体中 必须要有⼀个bizId(对于同⼀业务全局唯⼀,如⽀付ID、订单ID、帖⼦ID等)作为去重和幂 等的依据,避免同⼀条
消息被重复消费。

这个问题针对业务场景来答分以下⼏点:

1.⽐如,你拿到这个消息做数据库的insert操作。那就容易了,给这个消息做⼀个唯⼀主键, 那么就算出现重复消费的情况,就会导致主键冲突,避免数据库出现脏数据。

2.再⽐如,你拿到这个消息做redis的set的操作,那就容易了,不⽤解决,因为你⽆论set⼏次 结果都是⼀样的,set操作本来就算幂等操作。

3.如果上⾯两种情况还不⾏,上⼤招。准备⼀个第三⽅介质,来做消费记录。以redis为例,给 消息分配⼀个全局id,只要消费过该消息,将以K-V形式写⼊redis。那消费者 开始消费前,先去redis中查询有没消费记录即可。

消息怎么路由?

至少记住这句话:消息创建时会有路由key,队列也有路由key并绑定到交换器上,消息到交换器后俩路由key匹配,有匹配就投,没就丢。交换器也分扇出和直达和主题。不同的交换器绑定的路由key也有规则。

从概念上来说,消息路由必须有三部分:交换器、路由、绑定。⽣产者把消息发布到交换器 上;绑定决定了消息如何从路由器路由到特定的队列;消息最终到达队列,并被消费者接收。 消息发布到交换器时,消息将拥有⼀个路由键(routing key),在消息创建时设定。 通过队列路由键,可以把队列绑定到交换器上。 消息到达交换器后,RabbitMQ会将消息的路由键与队列的路由键进⾏匹配(针对不同的交换 器有不同的路由规则)。如果能够匹配到队列,则消息会投递到相应队列中;如果不能匹配到 任何队列,消息将进⼊ “⿊洞”。 常⽤的交换器主要分为⼀下三种:

direct:如果路由键完全匹配,消息就被投递到相应的队列
fanout:如果交换器收到消息,将会⼴播到所有绑定的队列上
topic:可以使来⾃不同源头的消息能够到达同⼀个队列。 使⽤topic交换器时,可以使⽤通配 符,⽐如:“*” 匹配特定位置的任意⽂本, “.” 把路由键分为了⼏部分,“#” 匹配所有规则 等。特别注意:发往topic交换器的消息不能随意的设置选择键(routing_key),必须是 由"."隔开的⼀系列的标识符组成。

消息如何分发?

若该队列⾄少有⼀个消费者订阅,消息将以循环(round-robin)的⽅式发送给消费者。每条 消息只会分发给⼀个订阅的消费者(前提是消费者能够正常处理消息并进⾏确认)。

消息基于什么传输?

由于TCP连接的创建和销毁开销较⼤,且并发数受系统资源限制,会造成性能瓶颈。 RabbitMQ使⽤信
道的⽅式来传输数据。信道是建⽴在真实的TCP连接内的虚拟连接,且每条 TCP连接上的信道数量没有
限制。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/654476.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号