群消息,究竟存1份还是多份? 糖太宗 • 2022年5月15日 下午9:20 • 技术 • 阅读 28 群消息,究竟存一份还是多份? 任何技术方案,都不是天才般灵感乍现想到的,一定是一个演进迭代,逐步优化的过程。今天就聊一聊,群消息,为啥只需要存一份。 群信息,用户信息,群成员关系都是基础数据: group_info(gid, group_info); user_info(uid, user_info); group_members(gid, uid); 假设一个群(gid)里有4个成员,其中: (1)三个在线(A, uid1, uid2); (2)一个不在线(uid3); A发送了一条消息,很容易想到,对于不同的群友消息存多份,每个群友一个队列来存储。但由于在线的用户会实时的收到消息,所以暂定只为离线的用户存储。 用户收到的群消息,也是基础数据: user_msgs(uid,msgid,gid,sender_uid,time,content); 很容易想到,整个群消息的发送流程如上图1-4: (1)发送消息; (2)查询状态; (3)不在线的存储离线; (4)在线的实时推送; “在线的群友不存储,离线的群友才存储”会带来的问题是,如果第四步发生异常,群友会丢失消息。 消息的可达性是聊天系统中最重要的要素(没有之一),故这个方案是不行的,需要优化为“不管是否在线,都要先存储”。 发送群消息的流程优化为,如上图1-4: (1)发送消息; (2)所有人都存一份; (3)查询状态; (4)在线的实时推送; 先将消息落地,能够保证消息可达性,那何时才能删除已经落地的群消息呢? 对于在线的群友,收到群消息后,给个ack确认,才能删除。 画外音:逻辑删除,还是物理删除,根据业务是否有消息漫游决定。 对于离线的群友,在下次登陆后,拉取完离线消息,再给ack确认,才能删除。 总之,为了保证消息的可达性,不管是在线消息,还是离线消息,必须接收方给ack确认,才能删除消息。 “不管是否在线,都冗余一份群消息”带来的问题是,同一条消息存储了很多次,对磁盘和带宽造成了很大的浪费。很容易想到的优化是:群消息实体存储一份,用户只冗余消息ID。 故基础数据可以由: user_msgs(uid,msgid,gid,sender_uid,time,content); 优化为: group_msgs(msgid,gid,sender_uid,time,content); user_msgs(uid, msgid, gid); 这个优化,对于消息投递,以及消息删除的核心流程没有影响,几个实践为: (1)在线用户投递消息实体,ack消息ID; (2)离线用户先拉取消息ID,再拉取消息实体,再ack消息ID; 如此这般,假如在某个群友A期间,群里陆续发送了N条消息,则user_msgs(uid, msgid, gid)里,会有 uidA -> mid1,mid2, mid3, … midN 等N条离线记录,拉取离线消息时,可以把这N条消息一次性拉取出来,然后再删除: delete from user_msgs where msgid in($mid1,$mid2…, $midN) and gid=$gid 然而,群消息具备“偏序”特性,上面的一次性删除完全可以优化为: delete from user_msgs where msgid >= $mid1 and gid=$gid 这就意味着,每个用户只需要记录“最近一次收到的消息ID”,而不用记录“所有未收到的消息ID集合”,每当收在线消息ack,以及拉离线消息ack时,只需要更新这个“最近一次收到的消息ID”即可。 于是乎,基础数据可以由: group_members(gid, uid); group_msgs(msgid,gid,sender_uid,time,content); user_msgs(uid, msgid, gid); 优化为: group_members(gid, uid, last_ack_msgid); group_msgs(msgid,gid,sender_uid,time,content); user_msgs(uid, msgid, gid); // 不再需要 即,群消息只存储一份,群友无需冗余任何消息实体,或者消息ID了。 对于在线的群友,收到群消息后,修改这个last_ack_msgid。 对于离线的群友,拉取群消息后,也修改这个last_ack_msgid。 画外音:这里的讨论,仅限于接收方收到了哪些消息,和发送方的已读回执没有关系。 总结 任何架构方案都不是灵光一现,而是逐步迭代优化产生的: (1)存多份,只存在线,消息容易丢; (2)存多份,所有群友都存储,消息冗余多; (3)存多份,只存ID,未利用偏序; (4)存一份,只存last_ack_msgid; 架构不只是设计出来的,更是演进出来的。 任何脱离业务的架构设计都是耍流氓。 架构师之路-分享技术思路 若有收获,随手转发。 相关文章: 《群消息已读回执(这个屌),究竟是推还是拉?》 发布者:糖太宗,转载请注明出处:https://www.qztxs.com/archives/science/technology/6829 赞 (0) 打赏 微信扫一扫 支付宝扫一扫 糖太宗 0 0 生成海报 很多人问,到底要不要转管理? 上一篇 2022年5月15日 下午9:20 我们从来都反对“大中台,小前台”的架构设计! 下一篇 2022年5月15日 下午9:21 相关推荐 技术 AWVS扫描器IAST使用 0x00 前言 很久没有更新blog了,这次把几个笔记分享一下,AWVS自带的IAST功能,很多人都不知道,这里记录一下IAST如何使用 0x01 扫描器部署 这里笔记写的较早,版本还是awvs13 1 2 3 4 5 6 7 8 9 10 # pull 拉取下载镜像 docker pull secfa/docker-awvs # 将Docke... 常山赵子龙 2022年6月13日 91000 proxychains_error 在使用proxychains代理时,报了个错误 1 2 ProxyChains-3.1 (http://proxychains.sf.net) ERROR: ld.so: object 'libproxychains.so.3' from LD_PRELOAD cannot be preloaded (cannot open shared object fi... 常山赵子龙 技术 2022年6月13日 31000 MYSQL常用函数 普通函数 数学函数 SELECT ABS(-8); /*绝对值*/ SELECT CEILING(9.4); /*向上取整*/ SELECT FLOOR(9.4); /*向下取整*/ SELECT RAND(); /*随机数,返回一个0-1之间的随机数*/ SELECT SIGN(0); /*符号函数: 负数返回-1,正数返回1,0返回0*/ 字符... 糖太宗 技术 2022年6月16日 23000 技术 惊叹!世界上最漂亮的排序算法! 直奔主题,世界上“最漂亮”的排序算法,只有6行。 void stooge_sort(int arr[], int i, int j){ if (arr[i]>arr[j]) swap(arr[i], arr[j]); if (i+1>=j) return; int k=(j-i+1)/3;... 糖太宗 2022年5月11日 26000 信息安全规划文档的编写 (转发) 0-00 安全规划的目的 组织安全部门建立到运行一段的时间,工作阶段从救火到标准化的过程就需要考虑编制和实施适合自己组织的安全规划了。 编写安全规划主要为以后的工作进行合理的估计和安排,从结构上提升组织的安全水平,大概是从杂牌军到正规军的转变,也有可能现有的方法满足不了实际业务的需求,需要重新规划、设计。 0-01 安全规划应该包含的内容 安全... 常山赵子龙 技术 2022年6月16日 28000 发表回复 您的电子邮箱地址不会被公开。 必填项已用*标注*昵称: *邮箱: 网址: 记住昵称、邮箱和网址,下次评论免输入 提交