发送到消息队列里的数据不见了,消息丢失

175 阅读4分钟

面试题

如何保证消息的可靠性传输(如何处理消息丢失的问题)?

面试官心里分析

这个是肯定的,用mq有个原则,就是数据不能多一条,也不能少一条,不能多就是消费重复消费和幂等性问题,不能少,就是说消息的可靠性传输。

面试题解析

rabbitMQ可能存在的数据丢失问题

  1. 写消息的过程中,消息都没到rabbitMQ在网络传输中就丢了,或者是消息到了rabbitMQ但是人家内部出错了没保存下来。
  2. rabbitMQ接收到消息之后暂存在自己的内存里,结果消费者还没有来及的消费rabbitMQ就自己挂掉了,就导致暂存在内存里的数据就给搞丢了。
  3. 消费者消费到了这个消息,但是还没来得及处理,就自己挂掉了,但是rabbitMQ以为这个消费者已经处理完了

对于第1种情况:生产者发送过程丢失了数据,可以选择使用rabbitMQ提供的事务功能,就是生产者发送数据之前开启rabbitMQ事务(channel.txSelect),然后发送消息,如果消息没有成功被rabbitmq接收到,那么生产者会收到异常报错,此时就可以回滚事务(channel.txRollback),然后重试发送消息,如果收到了消息,那么可以提交事务(channel.txCommmit)。但是问题来了,rabbitMQ事务机制一搞,基本吞吐量就下来了,因为它是阻塞的,太耗性能了。

所以一般来说,如果你要确保说写入rabbitMQ的消息不丢失,可以开启confirm模式,在生产者那里设置开启confirm模式后,你每次写入的消息都会分配一个唯一的ID,然后如果写入了rabbitMQ中,rabbitMQ会给你回传一个ack消息,告诉你这个消息接收到了,如果rabbitMQ没有接收到这个消息,会回调你一个nack接口,告诉你这个消息接收失败,你可以重发。

事务机制与confirm机制最大的不同在于,事务机制是同步的,你提交一个事务之后会阻塞在那儿,但是confirm机制是异步的,你发送个消息之后就可以发送下一个消息,然后那个消息rabbitMQ接收之后会异步回调你一个接口通知你这个消息接收到了。

所以一般在生产者这块避免数据丢失,都是用confirm机制的。

对于第二种情况:rabbitMQ丢失了数据,就是rabbitMQ自己弄丢了数据,你必须开启rabbitMQ的持久化,就是消息写入之后会持久化到磁盘中,哪怕是rabbitMQ自己挂了,恢复之后会自动读取之前存储的数据,一般数据不会丢失。除非极其罕见的是,rabbitMQ还没有持久化,自己就挂了,可能导致少量数据会丢失的,但是这个概率比较小。

设置持久化有两个步骤,第一个是创建queue的时候将其设置为持久化的,这样就可以保证rabbitMQ持久化queue的元数据,但是不会持久化queue里的真实数据,第二个是发送消息的时候将消息的deliveryMode设置为2,就是将消息设置为持久化的,此时rabbitMQ就会将消息持久化到磁盘上去。必须要同时设置这两个持久化才行。

而且持久化可以跟生产者那边的confirm机制配合起来,只有消息被持久化到磁盘之后,才会通知生产者ack了,所以哪怕是在持久化到磁盘之前,rabbitMQ挂了,数据丢了,生产者收不到ack,你也是可以自己重发的。

哪怕是你开启了持久化机制,也有一种可能,就是这个消息写到rabbitMQ之后还没有持久化,rabbitMQ就挂了,会导致数据丢失,概率极小

对于第三种情况:消费端弄丢了消息,主要是因为你消费的时候,刚消费到,还没处理,消费端就挂了,比如重启了,那么就尴尬了,rabbitMQ会认为你已经消费了,那么数据也就丢失了,解决办法,关闭自动应答,采用手动应答机制。