热门标签 | HotTags
当前位置:  开发笔记 > 后端 > 正文

服务器接收消息写日志,IM服务器设计消息存储

这部分专门讲述IM消息存储的设计。消息存储的难度在于,要考虑以下的场景:离线消息存储。即发送消息时对方不在线该怎么处理。单聊、群聊消息。随着用户量越来越

这部分专门讲述IM消息存储的设计。消息存储的难度在于,要考虑以下的场景:

离线消息存储。即发送消息时对方不在线该怎么处理。

单聊、群聊消息。

随着用户量越来越大,应该以后如何扩展。

读扩散 VS 写扩散

消息同步模型中,有写扩散和读扩散这两种模型。在开始讨论之前需要先了解两个相关的概念:

收件箱(inbox):该用户收到的消息。

发件箱(outbox):该用户发出的消息。

写扩散(push)

写扩散就是经常说的push模式,即每个消息都直接发送到该用户的收件箱中。其优缺点如下:

优点:读优化,用户每次只需要去读取自己收件箱中的消息即可。

缺点:写很重,如果这个消息是一条群消息,那么一个群成员发送出去的消息将拷贝到所有其余群成员的收件箱中。

095647450c2c78f60e9b22546b1d19fd.png

读扩散(pull)

读扩散就是pull模式,用户每次到消息发送者的发件箱去拉取消息,优缺点如下:

优点:写优化,每次发送的消息只需要写到一个地方,由收件者自己去拉取消息即可。

缺点:读操作很重,假设一个用户有一千个好友,重新登录时需要拉取这些好友所有的离线消息。

cd296e7b5ded68e6117fc3035e7de52f.png

最终选择的是以pull模式为主的模式,理由在于:

IM业务属于『写多读少』类型的业务,如果使用push模式,将造成消息的大量冗余。

pull模式读操作较重的缺陷可以通过其他方式来优化解决。

下面来看具体的设计。

表设计

在数据库设计中,仅使用一个发送消息表来存储消息的具体内容,而另外有一个消息接收表用来存储消息的ID信息而不是具体内容,这样用户查询消息时,大体流程如下:

首先拉取接收消息表中的信息。

根据接收消息表中的ID以及发送者ID信息到发送信息表来具体查询消息。

0e887b1a6cd96861b1dde8be4c81cf44.png

用户发送消息表

无论是单聊还是群聊消息,都使用这个表来存储发送出去的消息。

im_message_send(msg_id,msg_from,msg_to,msg_seq,msg_content,send_time,msg_type)

其中:

msg_id:消息ID。

msg_from:消息发送者UID。

msg_to:消息接收者。如果是单聊消息那么就是用户UID,如果是群聊消息就是群ID。

msg_seq:客户端发送消息时带上的序列号,主要用于消息排重以及通知客户端消息发送成功之用。

msg_content:消息内容。

send_time:消息发送时间。

msg_type:消息类型,如单聊、群聊消息等。

用户接收消息表

im_message_recieve(id,msg_from,msg_to,msg_id,flag)

其中:

id:这个表的ID,自增。

msg_from:消息发送者ID。

msg_to:消息接收者ID。

msg_id:消息ID,对应发送消息表中的ID。

flag:标志位,表示该消息是否已读。

接收消息表的信息并没有很多,因为主体部分如消息内容、发送消息时间等都在发送消息表中。

cf48cc213432aa079e391c269bd94ace.png

分库分表及访问策略

发送消息表,根据msg_from字段做为分库分表的依据,而接收消息表则使用msg_to字段做为分库分表的依据。

另外,还需要添加缓存将群聊消息进行缓存,缓存的key为msg_to和msg_id的组合,这样查询具体群聊消息的时候就可以根据组ID来查询一条具体的消息了。

以上需要对存储之上的业务层完全透明,因此加上一个db proxy来处理消息的读写,除了应付这套流程以外,proxy的引入还有这些好处:

无状态,可以横向扩展。

下面的存储服务扩缩容的时候,proxy可以感知到变化,而这些对上面的应用层而言都是透明的。

有了这一层proxy之后,消息的读写流程如下。

写消息

收到用户发送过来的消息,db proxy做如下处理:

根据msg_from查询到哪个存储服务存储该发送消息,写入消息,同时得到写入成功之后的消息ID。

如果这条消息是一个群聊消息,此时根据群ID以及第一步返回的消息ID,写一条键为群ID以及消息ID组合,而值为消息内容的缓存数据到缓存中。

根据msg_to查询是哪个存储服务存储该接收消息,写入这部分信息。

7b460466c64ef9d0b290853cc21a97ea.png

读消息

读消息的过程反之:

根据msg_to查询是哪个存储服务存储该接收消息,查询到该消息之后就知道对应的msg_id。

根据第一步查询到msg_from以及msg_id来去发送消息表中查询消息,如果是群聊消息的话,可以首先组合这两个字段到缓存中查询,查询不到再查询数据库。

如果上面第二步中的群聊消息,在缓存中没有查询到,需要一个策略来向缓存中写入一份该群聊消息。

如果接收到用户已读该消息的应答,那么还需要再次根据msg_to查询该消息将flag字段变成用户已读状态。

登录之后拉取离线消息的优化

在第一篇基础篇中已经给出了拉取离线消息的基本流程,在这里还需要进行一些优化。

在实际的应用中,离线的群聊消息并不是需要每次登录都完整拉取下来的,因此这里可以做一个优化:登录时针对群聊消息仅拉取每个群的未读消息数量,用于客户端的展示,而实际消息内容的加载,可以等到用户真的点到这个群查看消息或者可以后台加载,总之不影响登录主流程即可。

细化了消息存储部分之后的整体架构如下图所示:

e8311bddf307aca67a621f36e32c6d3c.png

总结

采用pull模式为主的消息发送存储方式。

为了解决pull模式的读消息较重的问题,引入了以下组件:

db proxy来解决整个读写逻辑,这部分对业务层完全透明,同时proxy可以感知下面存储服务的扩缩容变更等。

群聊消息根据消息ID以及群ID写入缓存一份,不必每次都到存储服务器上面拉取消息。

使用另一个消息计数表来存储未读消息数量,登录之后群聊消息仅展示未读消息数量,这部分群聊消息可以延迟拉取或者后台拉取不影响客户端登录主流程。



推荐阅读
  • 本文探讨了如何在PHP与MySQL环境中实现高效的分页查询,包括基本的分页实现、性能优化技巧以及高级的分页策略。 ... [详细]
  • 本文探讨了使用Python实现监控信息收集的方法,涵盖从基础的日志记录到复杂的系统运维解决方案,旨在帮助开发者和运维人员提升工作效率。 ... [详细]
  • Hibernate全自动全映射ORM框架,旨在消除sql,是一个持久层的ORM框架1)、基础概念DAO(DataAccessorOb ... [详细]
  • 本文详细介绍了 Redis 中的主要数据类型,包括 String、Hash、List、Set、ZSet、Geo 和 HyperLogLog,并提供了每种类型的基本操作命令和应用场景。 ... [详细]
  • 本文介绍了SIP(Session Initiation Protocol,会话发起协议)的基本概念、功能、消息格式及其实现机制。SIP是一种在IP网络上用于建立、管理和终止多媒体通信会话的应用层协议。 ... [详细]
  • CentOS下ProFTPD的安装与配置指南
    本文详细介绍在CentOS操作系统上安装和配置ProFTPD服务的方法,包括基本配置、安全设置及高级功能的启用。 ... [详细]
  • 视觉Transformer综述
    本文综述了视觉Transformer在计算机视觉领域的应用,从原始Transformer出发,详细介绍了其在图像分类、目标检测和图像分割等任务中的最新进展。文章不仅涵盖了基础的Transformer架构,还深入探讨了各类增强版Transformer模型的设计思路和技术细节。 ... [详细]
  • 七大策略降低云上MySQL成本
    在全球经济放缓和通胀压力下,降低云环境中MySQL数据库的运行成本成为企业关注的重点。本文提供了一系列实用技巧,旨在帮助企业有效控制成本,同时保持高效运作。 ... [详细]
  • 在Android应用开发过程中,开发者经常遇到诸如CPU使用率过高、内存泄漏等问题。本文将介绍几种常用的命令及其应用场景,帮助开发者有效定位并解决问题。 ... [详细]
  • 在使用 Nginx 作为服务器时,发现 Chrome 能正确从缓存中读取 CSS 和 JS 文件,而 Firefox 却无法有效利用缓存,导致加载速度显著变慢。 ... [详细]
  • 本文详细介绍了Oracle 11g中的创建表空间的方法,以及如何设置客户端和服务端的基本配置,包括用户管理、环境变量配置等。 ... [详细]
  • 软件测试行业深度解析:迈向高薪的必经之路
    本文深入探讨了软件测试行业的发展现状及未来趋势,旨在帮助有志于在该领域取得高薪的技术人员明确职业方向和发展路径。 ... [详细]
  • 问题描述现在,不管开发一个多大的系统(至少我现在的部门是这样的),都会带一个日志功能;在实际开发过程中 ... [详细]
  • 洛谷 P4009 汽车加油行驶问题 解析
    探讨了经典算法题目——汽车加油行驶问题,通过网络流和费用流的视角,深入解析了该问题的解决方案。本文将详细阐述如何利用最短路径算法解决这一问题,并提供详细的代码实现。 ... [详细]
  • 如何在PHP中安装Xdebug扩展
    本文介绍了如何从PECL下载并编译安装Xdebug扩展,以及如何配置PHP和PHPStorm以启用调试功能。 ... [详细]
author-avatar
手机用户2602918637
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有