生产环境中可能出现一条消息因为某种原因丢失,为了保证消息不会丢失,需要同时对生产者、消息队列、消费者做出处理.
1.消息在生产者丢失
原因:生产者发送消息成功,但MQ没收到该消息,消息在从生产者传输到MQ的过程中丢失,一般由网络不稳定造成。
解决方案:
RabbitMQ 中提供了 publisher confirm
机制来避免消息发送到 MQ 的过程中丢失的问题。消息发送到 MQ 以后,会返回一个确认结果给生产者,用于表示消息是否确认成功。该确认结果存在两种请求:
publisher-confirm
该类型是 发送者确认 ,存在两种情况
- 消息成功投递到交换机,返回
ack
- 消息未投递到交换机,返回
nack
publisher-return
该类型是 发送者回执 ,存在两种情况
- 消息投递到交换机,且成功分发到队列,返回
ack
- 消息投递到交换机,但未成功分发到队列,返回
nack
发送方采用消息确认机制,当消息成功被MQ接收到后,会给生产者发送一个确认消息,表示接收成功。RabbitMQ发送方确认模式有三种,普通确认、批量确认、异步确认。Spring整合RabbitMQ后只使用了异步监听确认模式
2. 消息在MQ中丢失
原因:消息发送到MQ后,消息还没被消费却在MQ中丢失了。比如MQ服务器宕机或者未进行持久化进行了重启。
解决方案:持久化交换机、队列、消息。MQ 默认是内存存储消息,我们可以通过开启持久化的功能来确保在 MQ 中的消息不丢失,确保MQ服务器重启时仍然能从磁盘恢复对应的交换机,队列和消息。然后我们把MQ做多台分布式集群,防止出现所有的MQ服务器挂掉~
3.消息在消费者丢失
原因:默认消费者消费消息时,设置的是自动恢复MQ收到了消息。MQ会立刻删除自身保存的这条消息,如果消息已经在MQ中被删除,但消费者的业务处理出现异常或消费者服务宕机,那么就会导致该消息没有处理成功从而导致消息丢失。
解决方案:
RabbitMQ 采取的机制是当确认消息被消费者消费后就会立即删除
那么如何确认消息已被消费者消费?那就还得依靠回执来确认,消费者获取消息后,需要向 RabbitMQ 发送 ack
回执,表明自己已经处理消息。其中 ack
在 AMQP 中有三种确认模式:
- manual:手动 ack,需要在业务代码结束后,调用 api 发送 ack
- auto:自动 ack,由 spring 监测 listener 代码是否出现异常,没有异常则返回 ack,反之返回 nack
- none:关闭 ack,MQ 在消息投递后会立即删除消息
消费者向MQ的回复我们设置成手动回复。当消费者出现异常或者服务宕机时,MQ服务器不会删除该消息,而是会把消息重发给绑定该队列的消费者,如果该队列只绑定了一个消费者,则该消息会一直保持在MQ服务器,直到消费者能正常消费为止。具体做法是设置手动ACK
总结:
- 开启生产者确认机制,确保生产者的消息能到达队列
确认机制包括 publisher-confirm
和 publisher-return
当未送达到 交换机 我们可以通过 publisher-confirm 返回的 ack
和 nack
来确认
当 交换机 未成功路由到 队列,我们可以通过 publisher-return
自定义的回调函数来确认,每个 RabbitTemplate 只能配置一个 ReturnCallback
- 开启持久化功能,确保消息未消费前在队列中不会丢失
持久化功能分为 交换机持久化
、队列持久化
和 消息持久化
,我们都需要将 durable 设置为 true
- 开启消费者确认机制最低为
auto
级别
消费者确认机制有三种类型:manual (手动确认)
、auto (自动确认)
、none (关闭 ack)
- 失败重试机制
我们手动设置 MessageResoverer
为 RepublishMessageRecoverer 方式,将投递失败的消息转到异常队列中,交由人工处理