RocketMQ消息丢失解决方案:同步刷盘+手动提交
前言
之前我们一起了解了使用RocketMQ事务消息解决生产者发送消息时消息丢失的问题,但使用了事务消息后消息就一定不会丢失了吗,肯定是不能保证的。
因为虽然我们解决了生产者发送消息时候的消息丢失问题,但也只是保证Broker正确的接收到了消息,实际上接收到的消息会保存在os cache中,如果此时broker机器突然宕机,os cache中的消息数据就丢失掉了。
而且就算是os cache中的消息已经刷盘到了磁盘中,如果磁盘突然就坏了,消息是不是也就丢失了。
所以我们还要考虑Broker如何保证消息不丢失。
Broker的消息丢失解决方案
说到这里,我们就进入主题了,首先解决临时存在os cache,而未刷新到磁盘导致的消息丢失问题,那么如何解决呢?
看过之前系列文章的小伙伴都知道,Broker是有两种刷盘机制的,同步刷盘和异步刷盘,详细内容可以回顾一下这篇文章:深入研究Broker是如何持久化的。
解决的方式就是把异步刷盘改为同步刷盘,具体操作就是修改一下broker的配置文件,将其中的flushDiskType配置设置为:SYNC_FLUSH,默认它的值是ASYNC_FLUSH,即异步刷盘。
调整为同步刷盘后,只要MQ告诉我们消息发送成功了,那么就说明消息已经在磁盘中了。
接下来就要解决磁盘坏了导致的消息丢失问题了。
这个问题其实也很好解决,只要我们使用RockerMQ的高可用集群模式就可以了,也就是说如果返回消息发送成功的响应,那就代表Master Broker已经把数据同步到了Slave Broker中,保证数据有多个备份。
这样一来就算是Master Broker突然宕机 ,也可以通过Dledger技术进行主从的自动切换,使用我们备份的数据,这其中的原理我们已经讲过了,小伙伴们可以自己去复习回顾一下。Dledger是如何实现主从自动切换的
Consumer的消息丢失解决方案
到这里,我们已经确保了生产者和Broker的消息不会丢失了,那么消费者处理消息的时候会不会导致消息丢失呢?
答案是肯定的。
比如说我们的积分系统拿到了消息,还未执行该执行的操作,先返回给broker这条消息的offset,说这条消息已经处理过了。然后突然宕机了,这就导致mq认为这条消息已经处理过了,而实际并没有处理,所以这条消息就丢失掉了。
对于Kafka和RabbitMQ来讲,默认的消费模式就是上边这种自动提交的模式,所以是有可能导致消息丢失掉的。
而RocketMQ的消费者有点不一样,它本身就是需要手动返回消息处理成功的响应的。
所以其实Consumer的消息丢失解决方案也很简单,就是将自动提交改为手动提交。
消息零丢失方案的优缺点分析
如果在系统中落地一套消息零丢失的方案,无论什么场景都保证消息的可靠性,这似乎听起来不错,这也是它的优点所在,保证系统的数据都是正确的,不会有丢失的情况。
但它有什么缺点呢?
首先,引入了这套解决方案之后,系统的复杂度变高了,想想事务消息的实现方式你肯定会这么觉得。
而且比较严重的缺点是,它会导致系统的性能严重的下降,比如原来每秒可以处理好几万条的消息,结果在引入消息零丢失这套方案之后,可能每秒就只能处理几千条消息了。
其实只要随便思考一下,就可以想明白这个问题。
事务消息的复杂性导致生产消息的过程耗时更久了,同步刷盘的策略导致写入磁盘后才返回消息,自然也会增加耗时,而消费者如果异步的处理消息,直接返回成功,整个流程的速度会更快。
所以说引入这么一套消息零丢失的方案,对于性能的影响还是很大的。
总结
其实关于消息零丢失的方案无外乎就这么多,所以本篇文章内容不是很多。
既然我们刚才聊了消息零丢失方案的缺点,那么就继续讨论一下,究竟什么场景下需要引入这套方案吧。
王子给大家介绍一下自己的想法。
一般我们对于跟金钱、交易以及核心数据相关的系统和核心链路,可以采用这套方案。
比如说我们文章中举的例子:支付系统、订单系统、积分系统。
而对于其他的没那么核心的场景,丢失一些数据问题也不大,就不应该采用这套方案了,或者说可以做一些简化,比如事务消息改成失败重试几次的机制,刷盘策略改为异步刷盘。
那么小伙伴们在平时的工作中,这套方案是怎么应用到生产环境中的呢?欢迎留言讨论。
往期文章推荐: