1. 消息丢失问题
1.1 哪些环节有消息丢失的可能性
- 生产者发送消息到Broker的过程中,存在数据的丢失,例如异步发送;
- 主从同步消息数据的时候,存在数据的丢失,异步复制;
- 消费者没有消费完消息,就提交消费进度,存在消息丢失的可能
- 如果不是同步刷盘,存在数据丢失的可能性
- 磁盘没有进行消息备份,磁盘损坏导致消息丢失
1.2 RocketMQ消息零丢失办法
🎯生产者使用事务消息机制
- 发送half消息
既可以确认RocketMQ服务器是否正常,又可以让Broker服务器知道有半事务消息还未处理完成。
- half消息如果写入失败
发送half消息存在问题,可以认为MQ服务存在问题,我们可以在执行本地事务中写一个数据库消息表,类似补偿服务,等MQ服务正常之后,再进行处理。
- half写入成功之后,RocketMQ服务挂了
这种情况也就是本地事务可能已经提交了,但是处于UNKNOW状态,等MQ服务重启正常之后,就会进入回查流程,只要数据库的数据正常,就可以Commit。
- 写数据库失败了
如果数据操作失败,可能是数据库崩了,需要一段时间才能恢复,我们找个地方把订单信息先缓存起来,返回UNKNOW状态,过一段时间MQ回查的时候,我们可以再次提交数据库。
- 下单成功之后,如何优雅的等待支付成功
在订单场景下,通常会要求下单完成后,客户在一定时间内,例如10分钟内完成订单支付,支付完成后才会通知下游服务进行进一步的营销补偿。
可以使用RocketMQ提供的延迟消息机制。往MQ发一个延迟1分钟的消息,消费到这个消息后去检查订单的支付状态,如果订单已经支付,就往下游发送下单的通知。而如果没有支付,就再发一个延迟1分钟的消息。最终在第十个消息时把订单回收。
也可以用事务消息。在下单时,给Broker返回一个UNKNOWN的未知状态。而在状态回查的方法中去查询订单的支付状态。这样整个业务逻辑就会简单很多。我们只需要配置RocketMQ中的事务消息回查次数(默认15次)和事务回查间隔时间 (messageDelayLevel),就可以更优雅的完成这个支付状态检查的需求。
🎯RocketMQ配置同步刷盘+Dledger主从架构保证MQ主从同步高可用
- 同步刷盘,才响应成功给客户端,保证消息不丢失。
- Dledger主从架构保证MQ主从同步高可用
🎯消费者端使用同步消费机制
处理成功再 ACK,失败就重试,重复要幂等。
🎯NameServer高可用
实际上NameServer集群不可能全部都挂了,假设它们全部都挂了会怎么样呢?
有很多人就会认为在生产者和消费者中都会有全部路由信息的缓存副本,那整个服务可以正常工作一段时间。其实这个问题大家可以做一下实验,当NameServer全部挂了后,生产者和消费者是立即就无法工作了的。
当所有的NameServer挂掉之后,我们需要降级来处理这个问题,多次尝试发送还是失败之后,可以将消息发送到(redis、文件)缓存下来,例如一个生产者消息表中,然后起动一个线程定时往RocketMQ发送,等待MQ服务器好了之后,可以第一时间获得消息。