MQ消息防止丢失(MQ的消息一致性) 生产者和消费者ACK,事务性消息,MQ持久化

188 阅读4分钟

MQ消息防止丢失:****

消息流程分为三大部分,每一部分都有可能丢失数据。

1.生产阶段:Producer通过网络将消息发送给Broker,这个发送可能会发生丢失,比如网络延迟不可达等;

2.存储阶段:Broker肯定是先把消息存到内存的,然后根据刷盘策略持久化到硬盘中,假如刚收到Producer的消息,在内存中了,但是异常宕机了,导致消息丢失

3.消费阶段:消费失败了其实也是消息丢失的一种变体把。

生产阶段解决方案:

一:有三种send方法,同步发送,异步发送,单向发送。我们可以采用同步发送的方式进行发送消息,发消息的时候会同步阻塞等待broker返回的结果,如果没有成功,则不会收到SendResult,这种是最可靠的。其次是异步发送,在调回方法里可以得知是否发送成功。单向发送(OneWay)是最不靠谱的一种发送方式,我们无法保证消息真正可达。

二:发送消息如果失败或者超时了,则会自动重试。默认是重试三次,可以根据api进行更改,比如改为10次

三:假设Broker宕机了,但是生产环境一般都是多M多S的,所以还是会有其他master节点继续提供服务,这也不会影响到我们发送消息,我们消息依然可达。因此比如恰巧发送到broker的时候,broker宕机了,生产者收到broker的响应发送失败了,这时候生产者会自动重试,这时候宕机的broker就被踢下线了,所以生产者会换一台broker发送消息

Broker存储阶段解决方案:

一:MQ持久化消息分为两种:同步刷盘和异步刷盘。默认情况是异步刷盘,Broker收到消息后会先存到cache里然后立马通知生产者是消息我收到且存储成功了,你可以继续你的业务逻辑了,然后Broker起个线程异步的去持久化到磁盘中,但是Broker还没持久化到磁盘就宕机的话,消息就丢失了。同步刷盘的话是收到消息存到cache后并不会通知生产者说消息已经ok了,而是会等待到持久化到磁盘中后才会通知生产者说消息完事了。这也保证了消息不会丢失,但是性能不如异步高。需要看业务场景取舍。

二:集群部署,主从模式,高可用

即使Broker设置了同步刷盘策略,但是Broker刷完盘后磁盘坏了,这回导致盘上的消息全丢了。但是如果即使是1主1从了,但是Master刷完盘还没来得及给Slave就磁盘坏了,所以我们还可以配置不仅是等Master刷完盘就通知生产者,而是等Master和Slave都刷完盘后才去通知生产者说消息ok了

消费者阶段解决方案:

一:消费者会先把消息拉取到本地,然后进行业务逻辑,业务逻辑完成后手动进行ack确认,这时候才会真正的代表消费完成。而不是说pull到本地后消息就算消费完了

二:消息消费失败自动重试。如果消费失败了,没有进行ack确认,则会自动重试,重试策略和次数(默认为15次)

MQ消息的一致性:****

MQ重复消费的问题:****

原因:

一:在系统B发送消息给RocketMQ的时候,当消息发送超时时,结果系统B重试,导致RocketMQ接收到了重复的消息

二:当RocketMQ成功接收到消息,并将消息交给消费者处理,如果消费者消费完成后还没来得及提交offset给RocketMQ,此时自己宕机或者重启了,那么RocketMQ没有接收到offset,就会认为消费失败了,会重发消息给消费者再次消费

解决方案:

通过幂等性来保证,只要保证重复消费的消息不对结果产生影响,就解决了这个问题

一:RocketMQ支持消息查询的功能,只要去RocketMQ查询一下是否已经发送过该消息就可以了,不存在则在发送,存在则不发送;

缺点:RocketMQ消息查询性能不是特别好,如果是在高并发的场景下,每条消息都在发送RocketMQ时都去查询一下,可能会影响接口性能

二:引入Redis,在发送消息到RocketMQ成功后,向redis中插入一条数据,如果发生了重试,则先去redis中查询一下该条消息是否已经发送了,存在的话就不重复发送消息了

缺点:在一些极端场景下,redis也无法保证消息发送成功之后,就一定能写入redis成功,比如写入消息成功而redis宕机,那么再次查询redis判读消息是否已经发送过了,是无法得到正确结果的