消息队列中间件消息丢失

分析消息中间件,只需要画一个草图就很清晰了:

消息中间件模型

所谓消息丢失,指的是从生产者产生一条消息后,消费者没有接收到,接收到以后是否消费成功不在本次讨论范围内。

首先明确一个概念,不是所有业务场景都对消息丢失绝对零容忍的,比如日志服务丢几条也没设么关系。我们这里只讨论在强一致性场景下,怎么保证消息不丢失。下面从消息中间件的三个角色入手分别看一下会有什么问题。

消息生产者

我们知道,通信有三种模式:onewayasyncsync,这三种模式就通信的可靠性而言依次提升。

oneway模式下类似于UDP协议,生产者只管生产

async模式下,消息可以批量发送从而提升吞吐量,与oneway不同的是,async模式可以拿到消息保存成功与否的结果,缺点是不能保证与业务所在的事务保持一致,比如业务处理成功了消息发送却失败了,当然,一般的消息中间件都有重试机制,通过多次尝试,基本可以保证消息不丢失;

而对于强一致性的业务场景,生产者必须采用sync的方式去调用,配合消息中间件的重试机制,让消息与业务事务保持一致。

消息存储服务

首先讨论单机模式。当生产者将消息送达至消息存储服务时,消息服务为了保证高性能,会采用mmap技术直接将数据写入到文件对应的pagecache中来批量写磁盘,而pagecache是否写入到磁盘,什么时候写入到磁盘都是由操作系统来调度的,也就是说当主机异常关机时有可能就会导致数据还未来得及落盘。这种就是异步写磁盘模式。

因此在强一致性业务中,需要开启同步写磁盘模式,也就是说每次接收到消息时,必须马上调用系统函数,将数据写入磁盘。我们知道磁盘的读写速度相对较慢,因此在同步写磁盘模式下消息系统的吞吐量会大打折扣,这种权衡取决于具体的业务场景。

生产环境中,为了避免数据丢失,消息存储服务都有数据冗余机制(RocketMQ是主备机制,Kafka是副本机制),假如我们按照上面的理论来部署集群服务,当主节点异常下线后,有可能刚刚写入的消息还未来得及同步到备用节点,也就导致了消息丢失。因此要想在存储服务集群中做到完全的不丢失,还需要主节点在写数据的同时至少保证一个从节点的数据也采用同步方式写入成功

消息消费者

消费者跟存储服务之间有个消费进度记录,也就是offset,这个offset也会影响到消息的可靠性。

当消费者获取到消息内容时,对于写offset有两种方案:先写offset再消费数据先消费数据再写offset。前者会导致offset写成功,但是消息消费失败),正常在应用层用户应该记录消费失败的消息,但是当发生系统断电这种意外的进程停止时,进程再启动后就会跳过未消费的消息。因此在消费端必须采用先消费再记录offset的方式。

以上第二种方式导致消息重复消费,针对重复消费,就要求应用层必须做好幂等处理了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容