如何保证消息不丢失
消息丢失场景
数据丢失在MQ中比较常见,一般丢失数据都是在跨网络的部分,比如1、2、4。
- 生产者发数据
- 消费者消费数据
- MQ内部主从同步
而MQ写数据到磁盘过程也是有丢失数据的可能的。
一般写数据到磁盘不会直接去写,而是利用操作系统的缓存,先写数据到缓存中,等待操作系统异步刷进磁盘。
比如 Prometheus 的 WAL 机制。
事务消息-生产者
使用事务消息能保证本地事务和写入MQ的事务一致性。
比如订单场景,只保证本地下订单和向MQ发消息的事务一致性。不会像MySQL一样保证数据库事务。
只是保证了业务的分布式一致性问题。
事务消息能保证生产者发送数据到MQ过程不会丢数据。也就是第1个问题。
异步消费-消费者
一般消费者消费完数据,会提交ACK给RocketMQ,然后RocketMQ会修改offset。
kafka是提交offset,而且可以配置是否自动提交。
这种同步推送ACK的机制,结合重试机制(网络故障),能够保证消费消息的过程是不会丢失的。
但是异步消费的情况,在消费到数据后,把数据丢到线程池,然后直接返回ACK。
这种情况可能出现消息丢失问题。(比如消费者机器重启,消息就丢了)
RocketMQ同步刷盘-操作系统
将RocketMQ刷盘方式改为同步,可以解决消息在刷盘过程丢失的问题。也就是第3个问题。
但是同步刷盘会导致写入效率变低,需要有取舍。
比如金融场景要求强一致的情况,可以采用同步刷盘,牺牲写入效率。
而其他场景强一致要求不高的情况下,可以用异步。
Dledger主从架构保证MQ主从同步时不会丢消息
Dledger搭建的集群中,能够实现主从切换和主从数据同步。
普通集群不能实现主从切换,即如果Master挂了,Slave不会转换为Master,进而导致这一组Broker不可用。
Dledger内部能保证数据在主从同步时不会丢失。
NameServer挂掉消息怎么保证不丢失?
当NameServer挂掉之后,RocketMQ没有Broker路由的功能,找不到对应的Broker,就会导致RocketMQ不可用。
这种情况就要做降级处理,如果RocketMQ不可用,在重试几次之后。要将订单信息存到其他地方,比如Redis、内存。等MQ恢复之后,再第一时间处理这些消息。
Dledger的主从同步
通过两阶段提交的方式保证文件在主从之间同步数据不会丢数据。
uncommitted阶段
- 数据提交给Leader Broker之后,会被标记为uncommitted状态。
- 然后通过 DledgerServer 将 uncommitted 消息发给 Follower Broker 的 DledgerServer。
commited阶段
Follower Broker 的 DledgerServer 收到 uncommitted消息之后,必须返回一个ack。
- 如果Leader Broker收到超过半数的 ack ,就会把消息标记为 committed状态。
- Leader Broker上面的DledgerServer会发送一个committed消息给每个Follower,让他们把消息改为committed状态。
方案总结
- 生产者使用事务消息保证。
- 消费者不要使用异步消息。
- Broker配置同步刷盘。
- Dledger主从架构保证主从同步数据不会丢。
- MQ挂了之后准备降级方案。
每个阶段保证不丢失数据的方案,都是牺牲了性能来做的。
在实际处理时要做适当选择。