消息中间件常见面试题(RabbitMQ)
MQ场景:
- 异步发送(验证码、短信、邮件)
- MySQL、Redis、ES之间的数据同步
- 分布式事务等
一、RabbitMQ
1.1 消息不丢失
提问:如果保证消息不丢失呢?
流程:生产者将消息发送给交换机,交换机发送给指定的消息队列中,消费者再从消息队列中取出消息
(1) 消息丢失的原因:
1. 生产者发送给交换机的过程中就丢失了消息,
2. 交换机发送到指定的队列中消息丢失了,
3. 消费者还没来得及消费消息的过程中丢失消息。
RabbitMQ中提供了publisher comfirm机制来避免消息发送到MQ的过程中丢失,消息发送到MQ以后,会返回一个结果给发送者,表示消息是否处理成功。
(2) 消息发送失败的后处理方式:
- 回调方法即时重发
- 记录日志
- 保存到数据库然后定时重发,成功发送后即刻删除表中的数据
(3) 开启消息持久化
因为MQ默认是内存存储消息,重启或者宕机会导致消息的丢失,所以要开启消息持久化的功能,保证缓存在MQ中的消息不丢失
1.交换机持久化
2.队列持久化
3.消息持久化
(4) 消费者确认
RabbitMQ支持消费者确认机制,即:消费者处理消息后可以向MQ发送ack回执,MQ收到ack回执后才会删除该消息(也就是确保消费者消息了消息,才从消息将消息队列中对应的消息删除)
SpringAMQP的配置三种确认模式:
- manual:手动ack,需要业务代码结束后,调用api发送ack
- auto:自动ack,由spring监测listener代码是否出现异常,没有异常则返回ack;抛出异常则返回nack
- none:关闭ack,MQ假定消费者获取消息后会成功处理,因此消息投递后立即被删除
利用Spring的retry机制,在消费者出现异常时进行本地重试,设置重试次数,当次数达到之后,如果消息任然失败,则将消息投递到异常交换机,由人工处理。
1.2 消息重复消费
当服务发送网络问题或服务宕机导致出现RabbiMQ消息的重复消费问题
解决方案:
给每条消息设置唯一的标识
幂等方案有分布式锁、数据库锁(悲观锁、乐观锁)
1.3 消息堆积
当生产者生成消息的速度大于消费者消费消息的速度,就会导致队列中的消息堆积,直到队列存储消息达到上限,之后发送的消息就会成为死信,可能会被丢弃,这就是消息堆积问题。
解决消息堆积的三种思路:
- 增加更多的消费者,提高消费速度
- 在消费者内开启线程池加快消息处理速度
- 扩大队列容器,提高堆积上限,采用惰性队列
惰性队列
- 接收到消息后直接存入磁盘而非内存
- 消费者要消费消息时才会从磁盘中读取并加载到内存
- 支持数百万条的消息存储
1.4 延迟队列(死信队列)
延迟队列:进入队列的消息会被延迟消费的队列
场景:超时订单、限时优惠、定时发布
延时队列 相当于 死信交换机 + TTL(生存时间)
消息超时未消费都会进入死信队列中,再通过对死信队列中的消息进行消费
1.5 高可用机制
RabbitMQ的镜像队列功能允许队列在不同的节点上拥有多个副本。如果主节点发生故障,一个副本可以自动升级为新的主节点。在主从同步完成之前,主节点就已经宕机,可能出现数据丢失。
出现数据丢失如何解决呢?
我们可以采用仲裁队列,与镜像队列一样,都是主从模式,支持主从数据同步,主从同步基于Raft协议,强一致性。
使用方式:
只需要在声明队列时指定这个是仲裁队列即可。