前言
在面试题系列文章中,笔者本着效率的原则,没有总结RabbitMQ相关的知识,但是当其他知识点都总结完毕后,我发现如果面试中针对我们实际使用的RabbitMQ进行深入原理的提问或者说说框架使用的注意事项,那么我大概率是无法回答出来的。并且到目前为止,针对RabbitMQ的框架,我们项目组仅仅做到基本使用,没有做深入的研究,这篇文章名曰面试题,其实也是借着面试题让我们更加深入的了解RabbitMQ这个伟大的消息队列产品,所以,何乐而不为呢?
RabbitMQ
-
使用RabbitMQ有什么好处?
- 解耦:无论用redis还是rabbitmq传递分布式系统之间的数据交互,核心目的都是解耦
- 异步:用内存队列还是rabbitmq,通过队列存储不需要立即处理的业务,排队执行,可以加快主流程的响应速度
- 削峰:如果瞬时大量的并发请求都走数据库,对数据库来说是灾难的
-
使用了消息队列会有什么缺点?
- 系统的可用性降低
- 增加了系统设计的复杂度
-
RabbitMQ 概念里的 channel、exchange 和 queue 是逻辑概念,还是对应着进程实体?分别起什么作用?
- exchange内部实现为保存着对应关系的查找表;
- queue具有自己的erlang进程;
- channel是实现了路由工作的实体,负责按照rounting_key把消息投递到queue中,在AMQP中规定,channel是建立在真实的TCP/IP连接之上的虚拟连接,为了共享底层的TCP/IP资源,但是需要确定自己唯一的id。
-
vhost 是什么?起什么作用?
vhost可以理解为一个mini的broker,在实际应用中,不同的业务也会选择创建不同的vhost来实现业务隔离。
-
消息怎么路由?实际生产中用到的是那种路由方式?
rabbitmq中如果想实现正确的路由,需要定义队列,交换器和绑定的路由键,当发送消息的时候也需指定路由键,然后根据由交换器匹配具体的队列。rabbitmq支持三种方式的路由:
- direct:路由键完全匹配
- fanout:广播到所有绑定队列上
- topic:和direct比,属于通配符匹配,可以匹配多个队列
实际的生产实践中,大部分队列我们使用的是direct方式来进行路由,针对一个生产者多消费者的需求,我们使用fanout路由方式。
-
如何确保消息正确地发送至RabbitMQ?
RabbitMQ使用发送方确认模式,即将channel设置为confirm模式,这样所有发送过去的消息都会被指派一个唯一ID,发送方需要写一个异步的确认回调方法,处理确认状态和未确认状态的后续业务处理。
实际项目中,因为读写器发送过来的消息大部分是重复的,并且丢失几条对整体的判断区别不大,所以我们没有选择发送方确认模式。
-
如何确保消息接收方消费了消息?
RabbitMQ的消费者需要针对每条消息做消息确认。
消费者在订阅队列时,可以选择指定autoAck属性,如果为true,消息发送之后自动确认,rabbitmq就会删除消息;如果为false,需要消费者手动确认,并且在连接没有断掉的情况下,rabbtimq没有超时机制,会一直等待确认;如果连接断掉,rabbitmq会将此消息重新入队等待投递给下一个消费者。
除了确认以外,消费者还可以拒绝这条消息或者批量拒绝。
实际业务开发时,考虑到读写器会上传重复的大量数据,所以丢失几条也没关系,所以使用了RabbitListener的自动确认模式。
-
如何避免消息重复投递或重复消费?
首先需要确定,无论是broker还是consumer都是有可能收到重复的消息的,并且rabbitMQ提供的传递质量标准为至少一次,所以,如果确保重复消息不会影响我们的业务是需要在应用层面进行处理的。
具体的手段就是通过幂等性来解决重复消费的问题
- 利用数据库的唯一约束来实现幂等性(每个消费操作存入数据库,当出现重复数据时,主键会重复,所以无法入库)
- 利用前置条件实现幂等性
-
消息积压了该如何处理?你们的生产项目中有遇到过消息积压的问题吗?
消息积压,一般来说不是生产者变快了,就是消费者变慢了,我觉得这个问题不仅仅局限于rabbitMQ,使用内存队列也是一样的。当遇到积压问题时,要应该按照上面的思路核查问题。
我们的项目中也遇到过消息队列的积压问题,核心还是在于消费者的消费速度太慢,最后核查消费逻辑中使用redis的部分设计不够合理,针对这部分不太变化的内容,其实可以使用内存实现相同的功能,修改后,单消费线程的处理速度显著提高。另外,为了保证可用性,rabbtimq也支持配置多线程来消费队列的数据。
-
如何解决丢数据的问题?
丢数据的问题,可以按照消息的流转路径来分析。
- 生产者到broker之间丢了,可以采用confirm机制进行重传
- broker自己挂了,通过配置集群持久化等机制来实现可靠性保证
- 通过设置消费者的手动确认机制来实现可靠性
-
什么是死信队列?
当以下几种情况发生时,消息就会变成死信:
- 队列已满
- 消息被拒绝,并且无法重新入队
- 消息中过期时间超时
当消息变成死信时,rabbitmq会把消息发送到死信交换器进而投递到死信队列中,我们的程序可以监控这个队列,及时发现异常情况。
-
你了解延迟队列吗?延迟队列可以用在什么业务场景中?
延迟队列指的是消息发出后,并不想让消费者立即消费,而是等到一定时间后再消费该消息。
虽然我们业务中没有使用到这个延迟队列,但是我大致了解延迟队列的应用场景。比如:
- 延迟30分钟的未支付订单需要进行异常处理
- 用户需要定时操作智能家居,这种也可以使用延迟队列实现
延迟队列可以使用 RabbitMQ的过期时间和死信队列实现,通过设置消息的过期时间,然后投递到普通队列中,超时之后就会被投递到死信队列,真正的消费者监听的是死信队列。
-
消息队列能否保证消息按时间顺序消费?(线程池如何保证按照时间顺序消费任务?)
这个问题产生的原因是,我们想通过多个消费者提高系统的吞吐量,但是又无法保证消费顺序。
我们可以通过拆分成多个queue,一个queue对应一个消费者,相同id的数据一定会导入到同一个queue中,这样既可以增大吞吐量,又可以保证顺序了。