RocketMq如何保证消息可靠性
如何保证消息可靠性
生产者丢失
对于生产者丢失的情况出现在程序发送失败抛出来之后没有做重试处理,或者说发送的过程是成功的,但是网络闪退,MQ并没有收到信息,这个时候消息发送就失败了。
由于同步发送的一般不会出现这样使用方式,所以我们就不考虑同步发送的问题,我们基于异步发送的场景来说。
异步发送分为两个方式:异步有回调和异步无回调,无回调的方式,生产者发送完后不管结果可能就会造成消息丢失,而通过异步发送+回调通知+本地消息表的形式我们就可以做出一个解决方案。以下单的场景举例。
- 下单后先保存本地数据和MQ消息表,这时候消息的状态是发送中,如果本地事务失败,那么下单失败,事务回滚。
- 下单成功,直接返回客户端成功,异步发送MQ消息
- MQ回调通知消息发送结果,对应更新数据库MQ发送状态
- JOB轮询超过一定时间(时间根据业务配置)还未发送成功的消息去重试
- 在监控平台配置或者JOB程序处理超过一定次数一直发送不成功的消息,告警,人工介入。
一般而言,对于大部分场景来说异步回调的形式就可以了,只有那种需要完全保证不能丢失消息的场景我们做一套完整的解决方案。
MQ 丢失
如果生产者保证消息发送到MQ,而MQ收到消息后还在内存中,这时候宕机了又没来得及同步给从节点,就有可能导致消息丢失。
比如RocketMQ:
RocketMQ分为同步刷盘和异步刷盘两种方式,默认的是异步刷盘,就有可能导致消息还未刷到硬盘上就丢失了,可以通过设置为同步刷盘的方式来保证消息可靠性,这样即使MQ挂了,恢复的时候也可以从磁盘中去恢复消息。
虽然我们可以通过配置的方式来达到MQ本身高可用的目的,但是都对性能有损耗,怎样配置需要根据业务做出权衡。
消费者丢失
消费者丢失消息的场景:消费者刚收到消息,此时服务器宕机,MQ认为消费者已经消费,不会重复发送消息,消息丢失。
在RocketMq中默认是需要消费者回复ack确认,若是说出现了消费者对这个数据完成了消费,但是没有返回ack确认信息,重发的机制会根据MQ的类型不同进行不同间隔的时间的重复发送。若是说出现了多次重复发送仍然不能够收到正确的Ack确定信息,就会进入死信队列。这个时候就需要外人干预处理。
消息去重(避免客户端重复消费)
去重原则:使用业务端逻辑保持幂等性
幂等性:就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生了副作用,数据库的结果都是唯一的,不可变的。
只要保持幂等性,不管来多少条重复消息,最后处理的结果都一样,需要业务端来实现。
去重策略:保证每条消息都有唯一编号(比如唯一流水号),且保证消息处理成功与去重表的日志同时出现。
建立一个消息表,拿到这个消息做数据库的insert操作。给这个消息做一个唯一主键(primary key)或者唯一约束,那么就算出现重复消费的情况,就会导致主键冲突,那么就不再处理这条消息。 ———————————————— 版权声明:本文为CSDN博主「敖 丙」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/qq_35190492/article/details/103341634
消息重复
消息领域有一个对消息投递的QoS定义,分为:
最多一次(At most once) 至少一次(At least once) 仅一次( Exactly once) QoS:Quality of Service,服务质量
几乎所有的MQ产品都声称自己做到了At least once。
既然是至少一次,那避免不了消息重复,尤其是在分布式网络环境下。
比如:网络原因闪断,ACK返回失败等等故障,确认信息没有传送到消息队列,导致消息队列不知道自己已经消费过该消息了,再次将该消息分发给其他的消费者。
不同的消息队列发送的确认信息形式不同,例如RabbitMQ是发送一个ACK确认消息,RocketMQ是返回一个CONSUME_SUCCESS成功标志,Kafka实际上有个offset的概念。
RocketMQ没有内置消息去重的解决方案,最新版本是否支持还需确认。
消息消费
RocketMQ没有真正意义的push,都是pull,虽然有push类,但实际底层实现采用的是长轮询机制,即拉取方式
感谢您的阅读,您的支持是我写博客动力。