面试突击:消息中间件之RabbitMQ

发布于:2025-05-21 ⋅ 阅读:(18) ⋅ 点赞:(0)

一:你们项目中哪里用到了RabbitMQ ?

难易程度:☆☆☆

出现频率:☆☆☆☆

我们项目中很多地方都使用了RabbitMQ , RabbitMQ 是我们项目中服务通信的主要方式之一 , 我们项目中服务通信主要有两种方式实现 :

  1. 通过Feign实现服务调用
  2. 通过MQ实现服务通信

基本上除了查询请求之外, 大部分的服务调用都采用的是MQ实现的异步调用 , 例如 :

  1. 发布内容的异步审核
  2. 验证码的异步发送
  3. 用户行为数据的异步采集入库
  4. 搜索历史记录的异步保存
  5. 用户信息修改的异步通知(用户修改信息之后, 同步修改其他服务中冗余/缓存的用户信息)
  6. 静态化页面的生成
  7. MYSQL和Redis , ES之间的数据同步
  8. ....

二:为什么会选择使用RabbitMQ ? 有什么好处 ?

难易程度:☆☆

出现频率:☆☆☆

选择使用RabbitMQ是因为RabbitMQ的功能比较丰富 , 支持各种消息收发模式(简单队列模式, 工作队列模式 , 路由模式 , 直接模式 , 主题模式等) , 支持延迟队列 ,惰性队列而且天然支持集群, 保证服务的高可用, 同时性能非常不错 , 社区也比较活跃, 文档资料非常丰富

使用MQ有很多好处:

  •  吞吐量提升:无需等待订阅者处理完成,响应更快
  •  故障隔离:服务没有直接调用,不存在级联失败问题
  •  调用间没有阻塞,不会造成无效的资源占用
  •  耦合度极低,每个服务都可以灵活插拔,可替换
  •  流量削峰:不管发布事件的流量波动多大,都由Broker接收,订阅者可以按照自己的速度去处理事件

使用MQ也有很多缺点:

  •  架构复杂了,业务没有明显的流程线,不好管理
  •  需要依赖于Broker的可靠、安全、性能

三:使用RabbitMQ如何保证消息不丢失 ?

难易程度:☆☆☆

出现频率:☆☆☆☆☆

消息从发送,到消费者接收,会经历多个过程 , 其中的每一步都可能导致消息丢失

针对这些问题,RabbitMQ分别给出了解决方案:

* 消息发送到交换机丢失 : 发布者确认机制 publisher-confirm

消息发送到交换机失败会向生产者返回ACK , 生产者通过回调接收发送结果 , 如果发送失败, 重新发送, 或者记录日志人工介入

* 消息从交换机路由到队列丢失 : 发布者回执机制publisher-return

消息从交换机路由到队列失败会向生产者返回失败原因 , 生产者通过回调接收回调结果 , 如果发送失败, 重新发送, 或者记录日志人工介入

*消息保存到队列中丢失 :

MQ持久化(交换机持久化, 队列持久化 , 消息持久化)

*消费者消费消息丢失 :

消费者确认机制 , 消费者失败重试机制

通过RabbitMQ本身所提供的机制基本上已经可以保证消息不丢失 , 但是因为一些特殊的原因还是会发送消息丢失问题 , 例如 : 回调丢失 , 系统宕机, 磁盘损坏等 , 这种概率很小 , 但是如果想规避这些问题 , 进一步提高消息发送的成功率, 也可以通过程序自己进行控制

设计一个消息状态表 , 主要包含 : 消息id , 消息内容 , 交换机 , 消息路由key , 发送时间, 签收状态等字段 , 发送方业务执行完毕之后 , 向消息状态表保存一条消息记录, 消息状态为未签收 , 之后再向MQ发送消息 , 消费方接收消息消费完毕之后 , 向发送方发送一条签收消息 , 发送方接收到签收消息之后 , 修改消息状态表中的消息状态为已签收 ! 之后通过定时任务扫描消息状态表中这些未签收的消息 , 重新发送消息, 直到成功为止 , 对于已经完成消费的消息定时清理即可 !

四、消息的重复消费问题如何解决的 ?

难易程度:☆☆☆

出现频率:☆☆☆☆☆

在使用RabbitMQ进行消息收发的时候, 如果发送失败或者消费失败会自动进行重试, 那么就有可能会导致消息的重复消费

解决方案:

 ·每条消息设置一个唯一的标识id

· 幂等方案

    token+redis

    分布式锁

    数据库锁(悲观锁、乐观锁)

五:如果有100万消息堆积在MQ , 如何解决 ?

难易程度:☆☆☆

出现频率:☆☆☆☆☆

解决消息堆积有三种思路:

*提高消费者的消费能力

使用多线程消费

* 增加更多消费者,提高消费速度

使用工作队列模式, 设置多个消费者消费同一个队列中的消息

* 扩大队列容积,提高堆积上限

使用RabbitMQ惰性队列

惰性队列:

接收到消息后直接存入磁盘而非内存

消费者要消费消息时才会从磁盘中读取并加载到内存支持数百万条的消息存储

六、RabbitMQ如何保证消费的顺序性 ?

难易程度:☆☆

 出现频率:☆☆☆

一个队列只设置一个消费者消费即可 , 多个消费者之间是无法保证消息消费顺序性的

七:RabbitMQ的延迟队列有了解过吗 ?

难易程度:☆☆☆

出现频率:☆☆☆

RabbitMQ的延迟队列有两种实现方案 :

  1. 使用消息过期TTL + 死信交换机

  2. 使用延迟交换机插件

八、RabbitMQ如何设置消息过期 ?

难易程度:☆☆☆出现频率:☆☆☆

RabbitMQ置消息过期的方式有两种 :

    1.为队列设置过期时间, 所有进到这个队列的消息就会具有统一的过期时间

@Bean
public Queue ttlQueue(){
return QueueBuilder.durable("ttl.queue") // 指定队列名称,并持
久化
.ttl(10000) // 设置队列的超时时间,10秒
.deadLetterExchange("dl.ttl.direct") // 指定死信交换机
.build();
}

    2.为消息单独设置过期时间

@Test
public void testTTLQueue() {
// 创建消息
String message = "hello, ttl queue";
// 消息ID,需要封装到CorrelationData中 CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
// 发送消息
rabbitTemplate.convertAndSend("ttl.direct", "ttl", message, correlationData);
// 记录日志
log.debug("发送消息成功");
}

注意 :

  1. 队列过期和消息过期同时存在 , 会以较短的时间为准
  2. RabbitMQ队列消息过期的机制是判断队列头部元素是否过期 , 如果队列头部消息没有到过期时间 , 中间消息到了过期时间, 这个消息也不会被自动剔除

九:什么情况下消息会成为死信 ?

难易程度:☆☆☆

出现频率:☆☆☆

当一个队列中的消息满足下列情况之一时,就会成为死信(dead letter):

  •  消费者使用basic.reject或 basic.nack声明消费失败,并且消息的requeue参数设置为false
  •  消息是一个过期消息,超时无人消费
  •   要投递的队列消息满了,无法投递

十:什么是死信交换机 ? 如何为队列绑定死信交换机 ?

难易程度:☆☆☆

出现频率:☆☆☆

死信交换机和正常的交换机没有什么不同 , 如果一个包含死信的队列配置了dead-letter-xchange

属性,指定了一个交换机,那么队列中的死信就会投递到这个交换机中,而这个交换机称为死信交换机

为队列绑定死信交换机 , 只需要设置队列属性dead-letter-exchange即可

十一:RabbitMQ的高可用机制有了解过吗 ?

难易程度:☆☆☆☆

出现频率:☆☆☆

RabbitMQ是基于Erlang语言编写,而Erlang又是一个面向并发的语言,天然支持集群模式。RabbitMQ的集群有两种模式:

  • 普通集群:是一种分布式集群,将队列分散到集群的各个节点,从而提高整个集群的并发能力。

* 会在集群的各个节点间共享部分数据,包括:交换机、队列元信息。不包含队列中的消息。

* 当访问集群某节点时,如果队列不在该节点,会从数据所在节点传递到当前节点并返回

* 队列所在节点宕机,队列中的消息就会丢失

  • 镜像集群:是一种主从集群,普通集群的基础上,添加了主从备份功能,提高集群的数据可用性。

  • 交换机、队列、队列中的消息会在各个mq的镜像节点之间同步备份。
  • 创建队列的节点被称为该队列的主节点,备份到的其他节点叫作该队列的

镜像节点。

  • 一个队列的主节点可能是另一个队列的镜像节点
  • 所有操作都是主节点完成,然后同步给镜像节点
  • 主节点宕机后,镜像节点会替代成新的主节点


网站公告

今日签到

点亮在社区的每一天
去签到