问题概览
目前主流的消息队列技术(MQ技术)分为RabbitMQ和Kafka,其中深蓝色为只要是MQ,一般都会问到的问题。浅蓝色是针对RabbitMQ的特性的问题。蓝紫色为针对Kafka的特性的问题。
MQ的应用场景
MQ主要提供的功能为:异步 解耦 削峰 。
展开来讲就是
- 异步发送(验证码、短信、邮件...)
- MYSQL和Redis/ES之间的数据同步
- 分布式事务
- 削峰填谷
RabbitMQ如何保证消息不丢失
RabbitMQ的工作流程应该如下,其中每个环节都可能导致消息丢失。
publisher叫做发布者,也可叫做生产者。consumer叫做消费者。
生产者确认机制(解决消息未到达交换机或队列的问题)
生产者确认机制是用来确认生产者将消息发送给交换器,交换器传递给队列的过程中,消息是否成功投递的。发送确认分为两步,一是确认是否到达交换器,二是确认是否到达队列。
如果没有成功到达交换器,那么就会返回一个nack publish-confirm
如果没有成功到达队列,那么就会返回一个ack publish-return
那确认消息发送失败之后如何处理捏?
一般有以下三种做法:
- 回调方法即时重发
- 记录日志
- 保存到数据库然后定时重发,成功发送后即刻删除表中的数据
持久化技术(解决消息在队列中丢失的问题)
MQ默认是内存存储消息,需要持久化。分为交换机持久化、队列持久化和消息持久化,都通过代码来完成。
消费者确认机制(解决消息未到达消费者的问题)
RabbitMQ支持消费者确认机制,即:消费者处理消息后可以向MQ发送ack回执,MQ收到ack回执后才会删除该消息。而SpringAMQP则允许配置三种确认模式:
- manual:手动ack,需要在业务代码结束后,调用api发送ack。
- auto:自动ack,由spring监测listener代码是否出现异常,没有异常则返回ack;抛出异常则返回nack
- none:关闭ack,MQ假定消费者获取消息后会成功处理,因此消息投递后立即被删除。
在实际生产场景中使用的是auto模式。
如果确认消息丢失了怎么办捏?
我们可以利用Spring的retry机制,在消费者出现异常时利用本地重试,设置重试次数,当次数达到了以后,如果消息依然失败,将消息投递到异常交换机,交由人工处理。
常见问题和回答模板
RabbitMQ如何保证消息不丢失?
回答:(背熟以下回答大概用时2min)
RabbitMQ分别提供了三个机制,第一个是生产者确认机制,用来保证消息成功到达交换机,且成功到达队列。第二个是持久化机制,用来保证消息不在队列中丢失。第三个是消费者确认机制,用来保证消息成功到达消费者。通过以上三种机制可以综合保证消息不丢失。
首先是生产者确认机制,如果消息未能成功到达交换机,那么就返回nack publish-confirm,如果消息未能成功到达队列,那么就返回ack publish-return。通过返回的信号不同,我们可以判断具体是哪个环节出了问题并采取对应手段,譬如:用回调方法即使重发或者保存到数据库然后定时重发等。
然后是持久化机制,因为MQ是基于内存的存储,所以我们需要持久化来保证消息不会在队列中丢失。我们一共需要持久化三部分,分别是交换机、队列和消息本身,都是通过代码完成的。
最后是消费者确认机制,只有消费者接受到消息并返回ack信号后,RabbitMQ才会删除该消息。框架一般支持自动确认、手动确认和不确认三种模式。我们一般选的都是自动确认。如果确认发现消息在这个环节丢失,那么也会有相应的retry机制。