JAVA常见面试题总结(十一)RabbitMQ

1、RabbitMQ 的使用场景

  1. 跨系统异步通信。
  2. 多个应用之间的耦合,由于消息是平台无关语言无关的,而且语义上也不是函数调用,因此更适合作为多个应用之间的松耦合的接口。基于消息的耦合不需要双方系统使用同样的语言开发,甚至不需要同时在线。
  3. 应用内同步变异步,比如订单的处理,可以由前端应用将订单信息放入队列,后端应用从队列里依次获取消息进行处理,高峰时的大量订单可以积压在队列里慢慢处理掉。由于同步通常意味着阻塞,大量的线程阻塞会降低计算机的性能,通过消息队列变为异步之后将提升计算机的性能(分散高峰期计算压力)。
  4. 消息驱动架构,系统分解为消息队列、消息消费者、消息生产者,一个处理流程可以根据需要拆分为多个阶段,阶段之间用消息队列连接起来,前一个阶段的处理结果放入消息队列,后一个阶段从队列中取出消息继续处理。
  5. 应用需要更灵活的耦合方式,如发布订阅,如指定路由规则等。
  6. 跨局域网、甚至跨城市的通信。

2、RabbitMQ 有哪些重要的角色?

RebbitMQ 的主要角色有:生产者、代理和消费者。

  • 生产者(Procducer):消息的创建者,负责创建并将数据推送到消息服务器。

  • 代理(Broker):就是 RabbitMQ 服务本身,它是一个“通道”,本身不生产消息,只是负责将消息送达到消费者手中。

  • 消费者(Consumer):消息的接收方,负责处理数据和确认消息。

3、 RabbitMQ 有哪些重要的组件?

  • ConnectionFactory(连接管理器):应用程序与RabbitMQ之间建立连接的管理器。

  • Channel(信道):消息推送使用的通道。

  • Exchange(交换器):用于接收、分配消息。

  • Queue(队列):用于储存生产者的消息。

  • RoutingKey(路由键):用于把生产者的消息分配到交换机上。

  • BindingKey(绑定键):用于把交换机的消息绑定到队列上。

4、RabbitMQ 中的 vhost 的作用是什么?

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

5、怎么保证消息消费的幂等性?

保证消息消费的幂等性就是保证消息不会被重复消费。

业界主流的做法就是给消息加一个全局ID,在消费完成后将消息ID记录到数据库或Redis,每次消费前要先判断此ID的消息是否已经被消费了,如果已经被消费了就忽略这条消息,这样就能保证消息不被重复消费了。

6、RabbitMQ 怎样避免消息丢失?

消息丢失分为三种情况,分别是生产者丢失消息、RebbitMQ 服务器丢失消息、消费者丢失消息。

  1. 生产者丢失消息

    生产者丢失消息一般有两种因素:

    • 外部因素:网络丢包、网络故障等。

      1)使用事务机制(效率低,不推荐)

      2)RabbitMQ 提供了发送方确认机制(publisher confirm)(推荐)

      生产者通过channel.confirmSelect方法将信道设置为confirm模式,一旦信道处于confirm模式,该信道上所有的消息都会被指派一个唯一的ID(从1开始),一旦消息投递到所有匹配的队列后,RabbitMQ就会发送一个确认(Basic.Ack)给生产者,这就使的生产者知晓消息已经成功的发送了。

    • 配置问题导致交换机没有匹配的队列。

      1)使用 mandatory 设置为 true

      2)利用备份交换机(alternate-exchange)实现没有路由到队列的消息。

  2. RabbitMQ 服务器丢失消息

    设置消息持久化,将消息持久化到磁盘。

    设置集群镜像模式。

  3. 消费者丢失消息

    消息的补偿机制,手动确认机制(ACK)。

7、怎么才能保证消息成功持久化(持久化成功的条件)?

  1. 声明消息队列(Queue)持久化,durable=true
  2. 声明交换机(Exchange)持久化,durable=true
  3. 将发送的消息设置持久化,MessageProperties.PERSISTENT_TEXT_PLAIN
  4. 消息到达了持久化交换机和持久化队列

保证以上四个条件成立就可以保证消息持久化成功。

8、说一说消息持久化的缺点。

持久化的消息需要保存到磁盘中,磁盘的速度比内存的速度低得多,所以会降低消息服务器的吞吐量。

使用SSD硬盘可以缓解此问题。

9、怎么处理消息积压问题?

首先找到出现消息积压的原因:

原因一:消息的发送速率远远大于消息的消费速率

解决方式:

  1. RabbitMQ 的消费者默认都是单线程运行的,可以通过设置消费者的 concurrency 属性来增加消费者的消费线程数,从而增加消费速率。
  2. 另一种方式就是增加消费者部署的服务数量来增加消费者的数量,从而增加消费速率。

原因二:消费者出现问题无法消费消息,导致消息大量堆积

解决方式:

首先修复消费者的问题,如果不能短时间解决,可以先写一个简单的消费者,将消息取出来放到数据库中或扩容过的RabbitMQ中,然后等真正处理业务的消费者的问题修复完毕后再重新处理这些消息。

10、RabbitMQ 有几种广播类型?

RabbitMQ 有三种广播模式:

  1. fanout(散型): 投递消息到所有绑定(bind)到此交换机(Exchange)的队列(Queue)。

  2. direct(直连): 根据消息携带的路由键(routing key)将消息投递给对应绑定键(binding key)的队列。

  3. topic(主题): 根据消息的路由键(routing key)将消息投递给绑定键符合规则的队列。

    例如:消息的路由键为 A.D.C ,Queue1 的绑定键为 A.*.* ,Queue2 的绑定键为 *.B.* ,Queue3 的绑定键为 *.*.C 。那么最终消息将被投递到 Queue1 和 Queue3 。

11、RabbitMQ 怎么实现延迟消息队列?

JAVA常见面试题总结(十一)RabbitMQ

详细实现可参考以下文章:RabbitMQ的死信队列实现消息的延时消费

12、RabbitMQ 集群有什么用?

  1. 高容量:提升消息容量。
  2. 高可用:提升系统的可用性。

13、RabbitMQ 节点类型有哪些?

  • 内存节点:元数据(队列、交换机、绑定关系、用户、权限、vhost)信息保存在内存中,重启服务会导致这些信息丢失。
  • 磁盘节点:元数据信息保存在磁盘中,速度低于内存节点。集群中至少要有一个磁盘节点,而由于磁盘节点挂掉之后系统就不能再修改任何元数据信息,因此为保证集群可用性建议至少部署两个磁盘节点。

14、RabbitMQ 集群搭建需要注意哪些问题?

  1. 各节点之间使用“–link”连接,此属性不能忽略。
  2. 各节点使用的 erlang cookie 值必须相同,此值相当于“**”的功能,用于各节点的认证。
  3. 整个集群中必须包含至少一个磁盘节点。

15、RabbitMQ 每个节点是其它节点的完整拷贝吗?为什么?

不是。原因有以下两个:

  1. 存储空间考虑:每个节点如果有所有队列的完整拷贝,这样新增节点不但没有新增存储空间,反而增加了更多的冗余数据。
  2. 性能考虑:如果每条消息都要完整拷贝到集群中的每个节点,那么节点越多,拷贝数据需要的性能开销就越大,导致集群处理消息的能力越低。

16、RabbitMQ 集群中唯一一个磁盘节点崩溃了会怎么样?

集群中唯一一个磁盘节点崩溃了之后集群可以继续运行,但不能做以下事情:

  • 不能创建队列。
  • 不能创建交换机。
  • 不能创建绑定。
  • 不能添加用户。
  • 不能修改权限。
  • 不能添加和删除集群节点。

17、RabbitMQ 对集群节点停止顺序有要求吗?

有要求。应该先关闭内存节点,再关闭磁盘节点。否则可能会导致信息丢失。

18、如何保证消息消费的顺序?

保证消息消费顺序的方法有以下两种:

  1. 拆分成多个队列,每个队列对应一个消费者 ,生产者将需要保证先后顺序的一系列消息按顺序发送到同一个队列中,这样一个消费者就能按顺序消费消息了。

JAVA常见面试题总结(十一)RabbitMQ

  1. 只设置一个队列,这个队列只绑定一个消费者,消费者拿到消息之后并不直接消费,而是分发给多个执行器去执行,首先在内存中将消息分类,然后将需要消费顺序的消息发送给同一个执行器去执行。

JAVA常见面试题总结(十一)RabbitMQ