如何保证群聊消息不丢不重?背后的技术原理解析!

软件求生 2024-09-26 02:53:48



嘿,大家好,我是小米,一个29岁的技术爱好者,今天想跟大家聊聊一个非常有趣的话题——群聊消息如何保证不丢不重。这个问题在即时通讯应用里非常重要,尤其是当群人数众多,在线离线用户混杂时,如何让所有人都能够稳定、及时地收到消息,是一个不小的挑战。那我们今天就从技术角度,一起来看一看背后的逻辑与实现吧!

场景描述:群消息发送和接收流程

为了保证在线的用户能第一时间收到消息,离线的用户在上线后也不漏掉任何内容,我们需要一个高效可靠的系统来保障这一点。下面,我会带你一步步拆解这个过程。

步骤一:消息发送:首先,当用户X想要向群聊发送消息时,他会通过客户端发出请求,把这条消息推送到群聊的服务器(Server)。通常,我们会使用长连接或者WebSocket来保证消息的实时性。

步骤二:服务器查询群成员:群消息发送到服务器后,服务器首先要做的就是查询群聊的用户列表。假设我们有一群成员:X、A、B、C、D,Server会从数据库(DB)里把这些用户的信息查出来,包括每个用户的在线状态、消息记录等。

步骤三:查询在线状态:查询完群成员后,Server接下来会去缓存系统(如Redis)中检查每个用户的在线状态。这样,我们能快速知道哪些用户当前在线,哪些用户是离线的。在线的用户可以直接推送消息,而离线的用户则需要先存储消息,等他们上线再拉取。

步骤四:实时推送在线用户:对于查询结果中在线的用户A和B,Server会将群消息实时推送给他们。通过WebSocket或者推送服务,A和B几乎能立刻收到这条消息,体验上非常丝滑顺畅。

步骤五:存储离线用户的消息:对于离线的用户C和D,Server会将这条消息存储在数据库中,等他们下次登录的时候再发送。通常情况下,离线消息会存储在一个专门的离线消息表中。

问题一:离线消息存储的冗余问题

假设这个群有200个用户,其中180个是离线状态,如果每个离线用户都存储一份完整的群消息,这将极大增加数据库的存储压力。每个用户存储一条消息,这样会导致冗余存储。

解决方案1:使用msg_id代替完整消息内容

为了减少冗余存储,我们可以优化离线消息的存储方式。而不是为每个用户保存一份完整的消息,我们可以在离线消息表里只存储消息的msg_id。通过这种方式,所有的离线用户C、D……Z共享同一条群消息的内容,而每个用户只存储一条简化的msg_id。这样大大减少了数据库的存储压力。

离线消息表的结构可能是这样的:

当C和D重新上线时,Server会根据这个msg_id去查询消息的具体内容,然后推送给用户。

解决方案2:批量ACK和幂等性校验

虽然存储优化了,但是还有一个问题:消息丢失和重复接收。为了保证群消息不丢失、不重复,我们可以引入应用层的ACK机制。

ACK机制的引入

ACK是对消息接收的确认。Server发送消息后,会等待客户端返回一个ACK,确认该消息已成功接收。如果客户端在一定时间内没有返回ACK,Server会认为消息未达,重新发送这条消息。

每条群消息都要等待ACK确认,可能会产生大量的请求,给服务器带来巨大的压力。这时候,可以考虑批量ACK,即客户端一次性确认多条消息,而不是逐条确认,这样可以减少消息风暴对服务器的冲击。

批量ACK可以通过每隔一段时间或每当用户读取消息时触发。例如:

假设A收到消息后,不是每条消息都返回ACK,而是在收到多条消息后,一次性返回“我已经收到X条消息”的确认。

这种方式不仅能有效减轻服务器的负载,还可以避免消息丢失。

解决方案3:幂等性校验与客户端去重

为了避免消息重复接收,我们可以在服务端加入幂等性校验,即对于相同的消息,保证用户只接收到一次。在客户端,也要加入去重机制,确保同一条消息不会重复展示。

常用的做法是给每条消息加上唯一的msg_id,无论Server重发多少次,客户端在收到消息时,会先检查这条msg_id是否已经处理过。如果已经处理过,则忽略;如果没有,则显示消息,并返回ACK。

问题二:离线消息拉取过多,拉取过慢

对于离线用户来说,当他们重新登录时,可能会有大量的离线消息需要拉取。假设群聊里有几百条消息,用户可能会觉得拉取速度很慢。怎么办?

解决方案4:分页懒拉取

为了优化离线消息的拉取效率,我们可以使用分页懒拉取的方式。简单来说,就是用户重新登录时,不是一次性把所有消息都拉取下来,而是按需加载,比如每次拉取20条或50条。

这样用户可以先看到部分最新的消息,同时后台在继续加载更多的历史消息。这样不仅减少了初次拉取时的等待时间,也能减轻服务器的压力。

举个例子,离线用户C登录后,首先拉取到最后50条消息,等C浏览完这些消息时,系统会自动加载接下来的50条。这样的设计既不会影响用户体验,又能高效处理大量消息的拉取。

END

在群聊系统中,为了保证消息不丢失、不重复,我们需要对消息传递机制做一系列的优化:

在线用户通过实时推送及时收到消息;

离线用户则通过消息存储,在重新登录后拉取未读消息;

通过存储msg_id代替完整消息,减少存储冗余;

使用批量ACK机制,减少ACK风暴对服务器的影响;

加入幂等性校验和客户端去重,保证消息不重复;

分页懒拉取,优化离线消息的拉取速度。

通过这些方案,群聊系统不仅能保证消息不丢不重,还能在面对大规模用户时依然保持高效的表现。希望这篇文章对你有所帮助,大家如果有其他问题,欢迎留言交流哦!下次再见啦~

我是小米,一个喜欢分享技术的29岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!

0 阅读:25