如何解决高并发场景下的消息丢失问题

如何解决高并发场景下的消息丢失问题?

该问题有可能会以这样的方式出现:高并发场景下如何保证消息只会被消费一次

又或者说多次消费的结果和只消费一次是相同的

为了保证能够只被消费一次,那么我们需要保证消费不会被丢失。首先服务器发送消息到达消费者的时候每一个环节都有可能出现消息丢失的情况

image-20240903021245951

生产者出现消息丢失:

消息队列通常和生产者不在同一个服务器上,他们通过内网链接。如果出现网络波动则容易出现消息丢失。为了保证能够成功传输,需要在出现传输失败的时候能够进行重传,同时开启一个ACK确认机制。当消息队列成功接收到生产者的消息,需要同时回传一个ACK包给生产者标识已经成功接受,否则需要重新传输。

消息队列出现消息丢失的问题:

主要出现的原因是因为存在一个消息刷盘的过程,这里存在异步刷盘和同步刷盘的区别。如果开启了异步刷盘,那么接收到的消息会先存储到cache内存里面而没有存储进入磁盘。如果此时消息队列所在服务器出现重启(没有进行持久化),那么就会出现丢失。解决方案是使用同步刷盘机制。

image-20240903022151809

消息队列到消费者之间出现消息丢失:

消费者没有消费完成提前将进度更新到消息队列

生产者发送多条重复的消息,消费者需要保证只能消费一次:

主要就是使用一个记录表,来记录消费的状态

image-20240903022703021

当消费开始的时候需要将消息插入记录表中(或者使用Redis,但是需要注意缓存的三大问题还有数据库的同步问题),如果消费成功执行那么就在记录表中删除这条记录,并且发送延迟消息。如果插入记录失败说明之前已经存在这条记录,那么直接检查消费的状态即可。如果消息未完成则发送延迟消息并且重新开始消费。

如果业务代码执行出错,那么就需要手动开启异常,并且删除记录表之后重新开始消费。为了防止消息未完成并且新的消息无法进行消费,则需要添加一个扫描任务,自动删除那些长期未完成的任务。