几万条群离线消息,如何高效拉取,会不会丢?

简介: 群离线消息一般采用拉取模式,只存一份,不需要为每个用户存储离线群msg_id,只需存储一个最近ack的群消息id/time;为了保证消息可达性,在线消息和离线消息都需要ACK;离线消息过多,可以分群拉取、分页拉取等优化;如果收到重复消息,需要msg_id去重,让用户无感知。

image.png

继续答球友提问:

(1)群离线消息是推还是拉?

(2)几万条群离线消息,怎么保证不丢失?

 

群离线消息,是推还是拉?

关于写扩散、读扩散的问题,之前专门撰文写过,今天不直接同步结论,重点说说设计的思考过程

画外音:结论不如思路重要。


假如群离线是推,流程应该如何?会遇到什么问题?

先看看群离线消息的核心数据结构。


群成员表

t_group_users(group_id, user_id)

画外音:用来描述一个群里有多少成员


群离线消息表

t_offine_msgs(user_id, group_id, sender_id,time, msg_id, msg_detail)

画外音:用来描述一个群成员的离线消息。 


推,写扩散,存储群离线消息的过程如何?

(1)先从群成员表中,获取群里有多少个用户;

(2)从某个服务中,获取这些用户有多少个不在线;

(3)将群消息,插入到这些用户的群离线消息表;

画外音:如果要支持消息漫游,则可以省略步骤


此时,用户拉取离线消息的过程如何?

(1)用户登录,向server拉取离线消息;

(2)server返回并删除离线消息;


离线消息推,存在什么问题?

对于同一份群消息的内容,多个离线用户要存储很多份。假设群中有200个用户离线,离线消息则冗余了200份,这极大的增加了数据库的存储压力

 

如何优化,减少消息冗余量?

为了减少离线消息的冗余度,增加一个群消息表,用来存储所有群消息的内容,离线消息表只存储用户的群离线消息msg_id,就能大大的降低数据库的冗余存储量。


群消息表

t_group_msgs(group_id, sender_id, time, msg_id, msg_detail)

画外音:用来存储一个群中所有的消息内容


群离线消息表,需要进行优化

t_offine_msgs(user_id, group_id, msg_id)

画外音:优化后只存储msg_id

 

这样优化后,群消息的发送和存储做一些升级:

(1)每次发送群消息之前,先存储群消息的内容;

(2)每次存储离线消息时,只存储msg_id,而不用为每个用户存储msg_detail;

 

相应的,拉取离线消息也要做对应的升级:

(1)先拉取所有的离线消息msg_id;

(2)再根据msg_id拉取msg_detail;

(3)删除时,只删除自己的离线msg_id,而不删除msg_detail;

画外音:毕竟msg_detail只存储了一份,不能随便删。

 

上述过程,能保证离线消息的可达性么?

不能。


例如:server返回客户端离线消息之后,删除了离线消息,但客户端没有展现就奔溃了,离线消息就会丢失。


如何解决离线消息可达性呢?

很容易想到,通过ACK机制,server返回离线消息之后,不能立刻删除离线消息,而必须等客户端ACK,才能删除。


此时,离线消息拉取升级为:

(1)用户登录,向server拉取离线消息;

(2)server返回离线消息;

(3)客户端确认收到了离线消息;

(4)server再删除离线消息;

画外音:增加了3和4两个步骤。


还有一个问题,一次有几十个群,每个群有几千条离线消息,共计几万条群离线消息,消息量过大怎么办?


当然不能一次性拉取,可以:

(1)分群拉取

(2)每个群分页拉取

(3)拉取一页,删除一页,拉取下一页,删除下一页...


如果拉取了消息,却没来得及应用层ACK,会收到重复的消息么?

可以在客户端去重,对于重复的msg_id,对用户不展现,从而不影响用户体验。
image.png

如上所示,简单总结就是:

(1)群消息表存储消息实体msg_detail;

(2)群离线消息表,存每个用户的msg_id;

(3)分页拉取+应用层ACK,即保证性能,又保证消息可达性;

(4)客户端msg_id去重,保证用户体验;


上面讲的都是“推”模式,群离线消息的设计,真正线上应用较多的,是“拉”模式


推模式,存在什么问题?

对于离线的每一条消息,虽然只存储了msg_id,但是每个用户的每一条离线消息都将在数据库中保存一条记录有没有办法减少离线消息的记录数呢?


对于一个群用户,在ta登出后的离线期间内,肯定是所有的群消息都没有收到的,完全不用对所有的每一条离线消息存储一个离线msg_id,而只需要存储最近一条拉取到的离线消息的time(或者msg_id),下次登录时拉取在那之后的所有群消息即可,而完全没有必要存储每个人未拉取到的全部离线消息msg_id。


拉模式,需要对数据结构进行怎样的升级?

 

群成员表,增加一个属性:

t_group_users(group_id, user_id, last_ack_msg_id)

画外音:用来描述一个群里有多少成员,以及每个成员最后一条ack的群消息的msg_id(或者time)。


群消息表不变

t_group_msgs(group_id, sender_id, time, msg_id, msg_detail)

画外音:还是用来存储一个群中所有的消息内容


群离线消息表不再需要

 

使用拉模式后,群消息的发送和存储也要升级

(1)在消息msg_detail存储到群消息表后,不再需要操作离线消息表(之前需要将msg_id插入离线消息表);

(2)用户收到消息,应用层ACK后,将last_ack_msg_id更新(之前需要将msg_id从离线消息表删除);
image.png

群离线消息的拉取流程也类似:

(1)分页拉取离线消息;

(2)ACK离线消息;

(3)更新last_ack_msg_id;


总结

群消息还是非常有意思的,做个简单总结:

(1)群离线消息一般采用拉取模式只存一份,不需要为每个用户存储离线群msg_id,只需存储一个最近ack的群消息id/time;

(2)为了保证消息可达性,在线消息和离线消息都需要ACK

(3)离线消息过多,可以分群拉取、分页拉取等优化;

画外音:还可按需拉取,登录不拉取,点进群再拉取。

(4)如果收到重复消息,需要msg_id去重,让用户无感知;


思路比结论重要,希望大家有收获

本文转自“架构师之路”公众号,58沈剑提供。

相关实践学习
深入解析Docker容器化技术
Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。Docker是世界领先的软件容器平台。开发人员利用Docker可以消除协作编码时“在我的机器上可正常工作”的问题。运维人员利用Docker可以在隔离容器中并行运行和管理应用,获得更好的计算密度。企业利用Docker可以构建敏捷的软件交付管道,以更快的速度、更高的安全性和可靠的信誉为Linux和Windows Server应用发布新功能。 在本套课程中,我们将全面的讲解Docker技术栈,从环境安装到容器、镜像操作以及生产环境如何部署开发的微服务应用。本课程由黑马程序员提供。     相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
目录
相关文章
|
小程序 Windows
uniapp运行微信小程序routeDone的错误问题
uniapp运行微信小程序routeDone的错误问题
3023 0
|
人工智能 自然语言处理 搜索推荐
阿里云百炼产品月刊【2025年2月】
本期⽉刊主要亮点包括推出全新多模态理解生成大模型通义千问Omni系列,支持文本、图像、语音和视频输入,提供流式输出和四种自然对话音色,新增高性价比图生视频模型wanx2.1-i2v-turbo,生成速度快,耗时仅为旧模型的三分之一。此外,qwen-plus采购季资源包上线,享受8.6折优惠;qwen-max模型降价88%,极大降低使用门槛。智能体应用和工作流应用现支持DeepSeek系列模型,增强私有知识库问答和任务型、对话型工作流构建能力。文件交互和批量节点功能进一步提升应用灵活性和实用性。本月还推出了AI实训营和应用开发实训营,提供手把手AI课程和企业级多模态应用构建指导。
1542 0
|
机器学习/深度学习 存储
线性化注意力综述:突破Softmax二次复杂度瓶颈的高效计算方案
大型语言模型虽在各领域表现出色,但其核心的softmax注意力机制存在显著的计算资源消耗问题。本文探讨通过线性时间复杂度的替代方案突破这一瓶颈,介绍线性注意力机制、门控线性注意力及状态空间模型(SSM)等创新方法,旨在优化计算效率与内存容量之间的权衡,提升模型性能。
642 9
线性化注意力综述:突破Softmax二次复杂度瓶颈的高效计算方案
|
Docker 容器
docker 换国内镜像源,docker换源
docker 换国内镜像源,docker换源
11791 91
|
存储 人工智能 自然语言处理
从API到Agent:万字长文洞悉LangChain工程化设计
本文作者试着从工程角度去理解LangChain的设计和使用。大家可以将此文档作为LangChain的“10分钟快速上手”手册,希望帮助需要的同学实现AI工程的Bootstrap。
|
SQL 安全 网络安全
"守护数据王国,揭秘SQL权限管理与安全配置秘籍!从创建用户到加密技术,全方位打造铜墙铁壁,让你的数据库安全无忧,远离黑客侵扰!"
【8月更文挑战第31天】数据库是信息系统的核心,存储了大量敏感数据,因此确保其安全至关重要。本文详细介绍了SQL权限管理与安全配置的方法,包括理解权限类型、创建用户和角色、分配权限、实施密码策略、使用加密技术、配置防火墙、定期审计备份及防止SQL注入等,帮助你全面保护数据库安全。通过这些步骤,你可以有效管理和配置数据库权限,防范潜在威胁,确保数据隐私和完整性。
426 0
|
存储 数据库 开发者
Elasticsearch中的三种分页策略深度解析:原理、使用及对比
Elasticsearch中的三种分页策略深度解析:原理、使用及对比
|
存储 JSON JavaScript
一盏茶的时间,带你轻松上手Pinia
Pinia,让状态管理再无难题! 作为Vue.js官方推荐的新星级管理库,Pinia为开发者带来前所未有的顺滑体验。你还在为复杂难懂的状态管理代码头疼吗?别急,用Pinia你可以告别一切烦恼!
|
消息中间件 算法 物联网
RT-Thread快速入门-初探RT-Thread
RT-Thread快速入门-初探RT-Thread
565 0
|
Java Linux Android开发
嵌入式Android系统耳机驱动基本知识
嵌入式Android系统耳机驱动基本知识
394 0