群消息,究竟存1份还是多份?

2023-11-30 15:18
文章标签 消息 究竟 多份

本文主要是介绍群消息,究竟存1份还是多份?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

群消息,究竟存一份还是多份?

上一篇文章《群消息已读回执,究竟是推还是拉?》说,“很容易想到,是存一份”,被网友们骂了。

 

网友骂的对,任何技术方案,都不是天才般灵感乍现想到的,一定是一个演进迭代,逐步优化的过程。今天就聊一聊,群消息,为啥只需要存一份。

 

群信息,用户信息,群成员关系都是基础数据:

group_info(gid, group_info);

user_info(uid, user_info);

group_members(gid, uid);

假设一个群(gid)里有4个成员,其中三个在线(A, uid1, uid2),一个不在线(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);

 

这个优化,对于消息投递,以及消息删除的核心流程没有影响,几个实践为:

  • 在线用户投递消息实体,ack消息ID

  • 离线用户先拉取消息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。

画外音:这里的讨论,仅限于接收方收到了哪些消息,和发送方的已读回执没有关系。

 

总结

任何架构方案都不是灵光一现,而是逐步迭代优化产生的:

  • 存多份,只存在线,消息容易丢

  • 存多份,所有群友都存储,消息冗余多

  • 存多份,只存ID,未利用偏序

  • 存一份,只存last_ack_msgid

 

架构不(只)是设计出来的,更是演进出来的。

挖坑篇:《feed流,单聊群聊,系统通知,状态同步,到底是推还是拉?》

填坑篇1:《系统通知,究竟是推还是拉?》

填坑篇2:《状态同步,究竟是推还是拉?》

填坑篇3:《网页端消息,究竟是推还是拉?》

填坑篇4:《群消息已读回执,究竟是推还是拉?》

码字不易,求转。

这篇关于群消息,究竟存1份还是多份?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/437535

相关文章

SpringKafka消息发布之KafkaTemplate与事务支持功能

《SpringKafka消息发布之KafkaTemplate与事务支持功能》通过本文介绍的基本用法、序列化选项、事务支持、错误处理和性能优化技术,开发者可以构建高效可靠的Kafka消息发布系统,事务支... 目录引言一、KafkaTemplate基础二、消息序列化三、事务支持机制四、错误处理与重试五、性能优

SpringIntegration消息路由之Router的条件路由与过滤功能

《SpringIntegration消息路由之Router的条件路由与过滤功能》本文详细介绍了Router的基础概念、条件路由实现、基于消息头的路由、动态路由与路由表、消息过滤与选择性路由以及错误处理... 目录引言一、Router基础概念二、条件路由实现三、基于消息头的路由四、动态路由与路由表五、消息过滤

Spring Boot整合消息队列RabbitMQ的实现示例

《SpringBoot整合消息队列RabbitMQ的实现示例》本文主要介绍了SpringBoot整合消息队列RabbitMQ的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的... 目录RabbitMQ 简介与安装1. RabbitMQ 简介2. RabbitMQ 安装Spring

springboot rocketmq配置生产者和消息者的步骤

《springbootrocketmq配置生产者和消息者的步骤》本文介绍了如何在SpringBoot中集成RocketMQ,包括添加依赖、配置application.yml、创建生产者和消费者,并展... 目录1. 添加依赖2. 配置application.yml3. 创建生产者4. 创建消费者5. 使用在

如何通过Python实现一个消息队列

《如何通过Python实现一个消息队列》这篇文章主要为大家详细介绍了如何通过Python实现一个简单的消息队列,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录如何通过 python 实现消息队列如何把 http 请求放在队列中执行1. 使用 queue.Queue 和 reque

解读Redis秒杀优化方案(阻塞队列+基于Stream流的消息队列)

《解读Redis秒杀优化方案(阻塞队列+基于Stream流的消息队列)》该文章介绍了使用Redis的阻塞队列和Stream流的消息队列来优化秒杀系统的方案,通过将秒杀流程拆分为两条流水线,使用Redi... 目录Redis秒杀优化方案(阻塞队列+Stream流的消息队列)什么是消息队列?消费者组的工作方式每

使用C/C++调用libcurl调试消息的方式

《使用C/C++调用libcurl调试消息的方式》在使用C/C++调用libcurl进行HTTP请求时,有时我们需要查看请求的/应答消息的内容(包括请求头和请求体)以方便调试,libcurl提供了多种... 目录1. libcurl 调试工具简介2. 输出请求消息使用 CURLOPT_VERBOSE使用 C

Java中Springboot集成Kafka实现消息发送和接收功能

《Java中Springboot集成Kafka实现消息发送和接收功能》Kafka是一个高吞吐量的分布式发布-订阅消息系统,主要用于处理大规模数据流,它由生产者、消费者、主题、分区和代理等组件构成,Ka... 目录一、Kafka 简介二、Kafka 功能三、POM依赖四、配置文件五、生产者六、消费者一、Kaf

SSID究竟是什么? WiFi网络名称及工作方式解析

《SSID究竟是什么?WiFi网络名称及工作方式解析》SID可以看作是无线网络的名称,类似于有线网络中的网络名称或者路由器的名称,在无线网络中,设备通过SSID来识别和连接到特定的无线网络... 当提到 Wi-Fi 网络时,就避不开「SSID」这个术语。简单来说,SSID 就是 Wi-Fi 网络的名称。比如

SpringBoot 自定义消息转换器使用详解

《SpringBoot自定义消息转换器使用详解》本文详细介绍了SpringBoot消息转换器的知识,并通过案例操作演示了如何进行自定义消息转换器的定制开发和使用,感兴趣的朋友一起看看吧... 目录一、前言二、SpringBoot 内容协商介绍2.1 什么是内容协商2.2 内容协商机制深入理解2.2.1 内容