RocketMQ消息不丢失的解读
使用MQ消息丢失存在的场景
1. 只要跨网络肯定存在丢数据的风险,
1.1 生产者往MQ发消息
1.2 MQ往消费者发消息
1.3 主从同步的时候
2. 消息发过来要存硬盘,一般放到PageCache里,异步刷盘,会存在丢消息的可能。
3. 最坏的可能整个MQ都挂了
解决的办法
先来看生产者往MQ发消息
生产者往MQ发消息不能丢消息的分析
生产者使用事务消息机制保证消息零丢失
保证消息不丢失的目的:(下图)比如订单系统下单成功了,就往MQ发消息。如果没有成功,消息也不要发。保证的是订单系统和RocketMQ的一致性,保证的是整个事物消息的一般。
消息在下游服务是否处理完成是不管的。
half消息的作用就是探测RocketMQ是否存活,如果挂了就不去下单了,本地事务执行失败了,消息也不发送了。
本地Mysql下单,返回个状态给MQ。如果是commit发到下游,rollback就丢弃了。unknown就过一会回查。具体作用,比如订单系统下单了,5分钟之内必须进行支付,如果没有支付取消订单。
不会傻傻的等5分钟后去检查,会在这个5分钟之内多次检查,可能每隔几十秒就要检查1次。我们要去支付系统去查验,这个时候就用到了事务消息的检查状态。如果支付完成,MQ就返回comiit,保证检查状态在5分钟之内完成,5分钟到了订单取消。
关键是每一次检查都会有一个状态,通过这个状态有个反悔的机会。订单系统的每一次操作,都提供了1个反悔的机会
保证这个消息只要支付完成了,肯定会发到RocketMQ
MQ自己不能消息丢失
1. 刷盘的时候消息不能丢失
刷盘丢失就是因为异步刷盘(采用pageCache的缓存),导致可能会丢失,那么采取同步刷盘就可以了,每一个消息刷一次盘。在broker.conf配置
flushDiskType = SYNC_FLUSH
2. 主从同步的时候消息不能丢失
主从同步的时候,如果是异步复制,master写完之后就告诉生产者,然后再通知slave。如果master挂了,从节点就没有消息了。这中间可能会存在消息的丢失,也是可以通过配置同步的方式防止消息丢失。
async是异步复制,sync是同步复制。
另外使用Dledger主从架构可以保证在主从同步到时候不会丢失
Dledger会做3件事情
- Dledger会接管RokcetMQ的commitLog,
- 选举,不存在脑裂情况
- 阶段方式的同步
MQ发往消费者不能丢消息的分析
消息重复发送给消费着,可以保证消息不丢失,
有一种情况不行,如下面这种
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("please_rename_unique_group_name_4");
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs,
ConsumeConcurrentlyContext context) {
new Thread(){
public void run(){
//处理业务逻辑
System.out.printf("%s Receive New Messages: %s %n", Thread.currentThread().getName(), msgs);
}
};
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
消费者端不要使用异步消费机制,这种情况容易丢数据。
MQ挂了丢消息的分析
如果生产者往MQ发送,尝试了很多次发现一直失败,可以存到redis里,启动1个线程去扫描,尝试往MQ里去发,等MQ启动起来就可以发送成功了。
总结
每一个步骤都涉及到了分析,一定要结合实际情况求选择,综合考虑性能和可靠性。很多场景下可能并不需要保证消息的零丢失。比如说日志就可以允许丢消息,比如说对账就不能丢消息。