您当前的位置:首页 > 电脑百科 > 程序开发 > 架构

如何设计一个IM单聊架构

时间:2023-01-03 11:38:53  来源:  作者:IT架构师联盟

官方网站 www.itilzj.com 文档资料: wenku.itilzj.com

单聊

在众多的软件中,聊天功能是不可或缺的一个功能模块,或是用户和用户,或是用户和客服,都需要一个能够即时沟通的功能。

那么一个IM(InstantMessaging)的1对1聊天系统架构和存储应该如何设计呢。

下面来一步步的分析规划。

一. 功能点拆分

首先来看一个IM软件模块包括哪些基本功能

 

  •  

    会话列表(需要按照最后一条消息时间的倒序,将会话进行排列)

     

  •  

    聊天内容页(单聊双方的消息按时间顺序依次排列)

     

  •  

    未读消息计数(发送了但是没有读取的对话,需要在头像旁显示未读数字)

     

  •  

    用户头像,昵称(对话的用户资料)

     

 

根据上述功能点拆分后,可以确定下来需要哪些数据存储

 

  •  

    会话列表

     

  •  

    聊天的消息记录

     

  •  

    离线消息列表

     

  •  

    未读消息数据数量

     

  •  

    用户资料

     

二. 数据结构

 

实际进行下面几种数据结构存储时,可使用适合自己的场景的组件,例如公司自研的,或熟悉并满足场景要求的。

以下我拿redisMySQL来举例子,提供一个思路,实际生产环境还需要具体设计和选型

1. 会话列表

首先,需要为每一个会话创建一个会话Id进行标识。

再来看,会话列表的特性是新来消息的会话需要排在列表的上面,那么就可以使用一个有序集合SortedSet来存储。

结构如下:

 

key: prefix_xxx:{uid} value: {会话Id} score: {msgId}

 

key使用当前用户的uid来标识,集合中的每个item则是会话的Id,item的score为会话的最后一条消息的Id,这样根据score自动形成一个有序集合后,就能够满足我们的应用场景了。

2. 单聊消息列表

场景:聊天的消息列表,是一个按照时间顺序来排列的消息记录,并且需要可以根据offset来进行数据拉取。

同样可以使用redis的有序集合SortedSet来存储会话的消息列表,通过scan拉取消息

 

key: prefix_session_list:{sessionId} value: {msgId} score: {msgId}

 

也可以创建一个Mysql数据表来持久化存储消息记录

create table t_msg_record_list (`id` bigint not null primary key,`sessionId` bigint not null comment '会话Id',`msgId` bigint not null comment '消息Id',`isRead` tinyint not null default 0 commment '已读状态',`recordStatus` smallint not null default 0 commment '消息状态',`createTime` datetime not null,key `sessionId` (`sessionId`))engine=innodb;

 

根据会话Id分页查询时,就可以这样查询出所有msgId,再根据msgId去拉取msg的详情,组合成列表返回给客户端

SELECT msgId FROM t_msg_record_list WHERE sessionId = 1 AND recordStatus = 0 AND msgId > 1 ORDER BY id desc LIMIT 10;

3. 离线消息

离线消息可以分为「索引」和「消息id列表」两部分

离线消息索引需要记录的是,哪些用户给当前用户发送了离线消息,所以我们可以使用redis的集合Set来记录这些信息

 

key: prefix_xxx:{uid} value: {senderUid}

 

通过scan离线消息索引拿到了sendUid,再去拿这个会话的具体的离线消息id列表

然后,消息id列表使用redis的一个list链表来存储

 

key:prefix_offline_msg:{uid}:{senderUid} value:{msgId}

 

拿到所有msgId以后,去获取msg的实体详情填充即可

4. 未读计数

未读计数= 收到消息总数 - 已读数量

 

所以我们要存储两个已知数据便于计算出未读数量,即消息总数量和已读数量

由于对话存在双方发消息,所以分别维护对话双方的两个数据项,方便计算各自的未读数

接受消息总数量

 

key: prefix_session_count:{会话Id}:{uid} value: 总数量

 

已读数量

 

key: prefix_session_read_count:{会话Id}:{uid} value: 已读数量
5. 用户资料

 

使用mysql按需设计即可,变更保存后将数据同步到redis中使用

三. 架构层级拆分

如图所示,我们可以将架构大致分为五层,具体说明如下

1. 客户端层

我们IM服务的client肯定是有多个,web/App等,需要封装多种SDK隐藏底层细节,便于接入方接入。

2. 连接层

即时通讯需要客户端和服务端之间建立一个长链接,一方面维护用户的在线状态,另一方面便于复用连接进行消息的收发。

而维护连接这个动作,它的独立性很强,不需要与业务逻辑耦合,所以我们把链接层单独拆分出来一个。

这样在业务逻辑迭代上线时,业务层进行滚动上线也不会导致用户的链接断开。

连接协议

至于连接协议的选择,有如下几种方式

 

  1.  

    基于tcp链接,自定义传输协议(开发成本高,需要有一定条件)

     

  2.  

    websocket

     

  3.  

    http chunk (不建议使用,http工作在7层上,且只能服务端单向的向客户端传输数据,心跳连接不好维护)

     

 

这里推荐优先使用四层的协议来进行长链接的维护。

 

因为长链接集群的前方要做负载均衡,使用七层的协议,客户端要先和负载均衡机器建立链接,然后负载均衡机器再和业务层集群交互。
这样在连接数很大的时候,负载均衡的机器容易成为瓶颈。 四层的负载均衡可以直接通过修改目标机器ip prot的方式来进行转发,不需要client和负载均衡机器建链接
3. 业务层

 

业务层可以分为「长链接业务层」和「短链接业务层

 

具体两者的功能拆分,可根据业务实际情况设计
  •  

    长链接业务层: 负责会话相关的业务逻辑,比如收发消息/拉取会话列表/未读计数push等业务

     

  •  

    短链接业务层: 负责一些临时接口请求,比如用户资料拉取/资料变更等类似业务

     

 

两种业务层都通过调用服务层来进行数据读取和写入等擦欧总

4. 服务层

这层属于微服务,来为上层业务层提供基础服务能力,例如敏感消息过滤/会话列表数据读写/消息的落地和发送等功能。

5. 数据层

为上层的服务层来提供数据的实际落地写入,可以使用mysql,redis或其他sql/nosql数据库。

四. 推拉模式选择

那么在消息的发送上,我们应该选用推模式,还是拉模式,抑或是推拉结合呢?

1. 纯推模式

首先,我们假设使用纯推模式,来看会存在什么样的问题

场景1: 新设备登陆初始化

用户新登陆一台设备的时候,如果消息记录全都是空的,体验会很不好。

那么就需要服务端推送全量的消息记录到客户端,历史消息量大的时候,非常浪费服务端资源和带宽。

场景2: 设备间切换

 

tips:设备A和B都非第一次登陆

 

如图所示,流程如下

 

  1.  

    用户1在设备A上登陆,收到了用户2的消息1和2,push到了设备A上。

     

  2.  

    用户1退出了设备A,用户2又给他发送了消息3和4

     

  3.  

    用户1登陆了设备B,服务端push消息3和4到了设备B

     

 

但是此时,设备B缺少了消息1和2,用户再登陆回设备A的话又缺少了消息3和4,这也就产生了「消息空洞

2. 纯拉模式

然后,我们假设使用纯拉模式,来看会存在哪些问题

场景1: 收新消息

纯拉模式下,客户端需要和服务端进行一个长轮询,来定时检查是否存在新消息,并进行消息拉取。

这样轮询的时间间隔需要很难确定合适,间隔大了消息不实时,间隔小了无疑对服务器会产生很大的压力,无法支撑大量的在线用户进行聊天。

总结

由于推拉模式分别适用于业务中的不同场景需要,所以我们要使用推拉结合的方式来做。

拉模式适合的场景如下:

 

  1.  

    设备初始化时:先拉取会话列表,在根据会话的列表来为每个会话拉取一定的消息记录。可以通过控制拉取的数据量,减轻服务端压力。

     

  2.  

    历史聊天记录:按需拉取一定条数的记录,用户向上翻取记录再拉取固定条数的记录,直到翻到没有记录(就是翻页)。

     

 

推模式适合的场景如下:

 

  1.  

    用户实时接收消息

     

  2.  

    用户在线,有未读消息做通知栏push时

     

五. 消息流转

 

上面确定好推拉模式后,我们来看发消息和收消息都有哪些业务逻辑执行。

发消息

如上图所示,大致可分为三步

1. 消息过滤

首先用户的消息通过客户端的SDK发送出来,通过长链接到达了「逻辑层」,逻辑层接收到该请求后,可以根据定义的拦截过滤规则调用「服务层」的服务接口,来对消息进行处理;

2. 消息补充

处理通过后,来对消息的发送方资料进行填充,简单来说就是senderId标识,接收方接收消息时能够填充到对应的会话中。

3. 派发任务

消息实体处理完成后,将该消息push到「服务层」的「异步任务队列」服务中。

异步队列任务主要需要做以下四个方面的操作

 

  1.  

    更新存储端的「聊天记录」

     

  2.  

    更新会话的「消息总数量」,用来计算未读计数

     

  3.  

    根据接收方的在线状态来判断,是直接进行push,还是存入到离线列表中,等待用户上线后再进行消息拉取

     

  4.  

    更新「会话列表」的score值

     

具体异步队列还可以细化拆分,例如 实时任务队列 延时任务队列 失败重试队列 分别启动不同的线程池来消费任务,按需分配线程数处理

收消息

 

收消息主要有以下几个场景需要处理

 

  1.  

    客户端需要将消息append到聊天列表中,并在会话列表中将该会话增加未读消息标识。

     

  2.  

    如果接收方打开了开聊天窗口,客户端会发送一个消息的ACK给服务端,来标记该消息已读。

     

  3.  

    服务端收到已读ACK后需要更新「已读计数」相关数据项

     

  4.  

    如果是拉取离线消息,服务端还需要更新「离线消息」相关数据项

     

 

小结

本文从五个方面来对单聊的IM架构进行了设计分析

 

  1.  

    业务功能拆分

     

  2.  

    数据结构设计

     

  3.  

    系统结构设计

     

  4.  

    推拉模式选择

     

  5.  

    消息流转分析 讲了基础的结构有哪些,数据结构有哪些要求,以及消息流传的过程是什么样的。

     

 

对im单聊场景的开发框架有了大体的一个认识,但是实际落地的时候还有很多细节需要去实现。



Tags:架构   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
美团外卖宣布新一轮组织架构调整:提拔多位年轻管理者,年轻化、扁平化成主基调
新浪科技讯 4月11日上午消息,继2月下旬、3月下旬两轮人员调整后,美团到店到家的组织架构调整仍在继续。近日,美团外卖以内部邮件的方式宣布了新一轮的组织调整:外卖事业部下成立...【详细内容】
2024-04-11  Search: 架构  点击:(4)  评论:(0)  加入收藏
对于微服务架构监控应该遵守的原则
随着软件交付方式的变革,微服务架构的兴起使得软件开发变得更加快速和灵活。在这种情况下,监控系统成为了微服务控制系统的核心组成部分。随着软件的复杂性不断增加,了解系统的...【详细内容】
2024-04-03  Search: 架构  点击:(5)  评论:(0)  加入收藏
大模型应用的 10 种架构模式
作者 | 曹洪伟在塑造新领域的过程中,我们往往依赖于一些经过实践验证的策略、方法和模式。这种观念对于软件工程领域的专业人士来说,已经司空见惯,设计模式已成为程序员们的重...【详细内容】
2024-03-27  Search: 架构  点击:(13)  评论:(0)  加入收藏
哈啰云原生架构落地实践
一、弹性伸缩技术实践1.全网容器化后一线研发的使用问题全网容器化后一线研发会面临一系列使用问题,包括时机、容量、效率和成本问题,弹性伸缩是云原生容器化后的必然技术选择...【详细内容】
2024-03-27  Search: 架构  点击:(10)  评论:(0)  加入收藏
京东小程序数据中心架构设计与最佳实践
一、京东小程序是什么京东小程序平台能够提供开放、安全的产品,成为品牌开发者链接京东内部核心产品的桥梁,致力于服务每一个信任我们的外部开发者,为不同开发能力的品牌商家提...【详细内容】
2024-03-27  Search: 架构  点击:(13)  评论:(0)  加入收藏
从 MySQL 到 ByteHouse,抖音精准推荐存储架构重构解读
ByteHouse是一款OLAP引擎,具备查询效率高的特点,在硬件需求上相对较低,且具有良好的水平扩展性,如果数据量进一步增长,可以通过增加服务器数量来提升处理能力。本文将从兴趣圈层...【详细内容】
2024-03-22  Search: 架构  点击:(25)  评论:(0)  加入收藏
全程回顾黄仁勋GTC演讲:Blackwell架构B200芯片登场
北京时间3月19日4时-6时,英伟达创始人黄仁勋在美国加州圣何塞SAP中心登台,发表GTC 2024的主题演讲《见证AI的变革时刻》。鉴于过去一年多时间里AI带来的生产力变革,以及英伟达...【详细内容】
2024-03-19  Search: 架构  点击:(17)  评论:(0)  加入收藏
高并发架构设计(三大利器:缓存、限流和降级)
软件系统有三个追求:高性能、高并发、高可用,俗称三高。本篇讨论高并发,从高并发是什么到高并发应对的策略、缓存、限流、降级等。引言1.高并发背景互联网行业迅速发展,用户量剧...【详细内容】
2024-03-13  Search: 架构  点击:(6)  评论:(0)  加入收藏
有了LLM,所有程序员都将转变为架构师?
编译 | 言征 出品 | 51CTO技术栈(微信号:blog51cto)生成式人工智能是否会取代人类程序员?可能不会。但使用生成式人工智能的人类可能会,可惜的是,现在还不是时候。目前,我们正在见...【详细内容】
2024-03-07  Search: 架构  点击:(19)  评论:(0)  加入收藏
如何判断架构设计的优劣?
架构设计的基本准则是非常重要的,它们指导着我们如何构建可靠、可维护、可测试的系统。下面是这些准则的转换表达方式:简单即美(KISS):KISS原则的核心思想是保持简单。在设计系统...【详细内容】
2024-02-20  Search: 架构  点击:(36)  评论:(0)  加入收藏
▌简易百科推荐
对于微服务架构监控应该遵守的原则
随着软件交付方式的变革,微服务架构的兴起使得软件开发变得更加快速和灵活。在这种情况下,监控系统成为了微服务控制系统的核心组成部分。随着软件的复杂性不断增加,了解系统的...【详细内容】
2024-04-03  步步运维步步坑    Tags:架构   点击:(5)  评论:(0)  加入收藏
大模型应用的 10 种架构模式
作者 | 曹洪伟在塑造新领域的过程中,我们往往依赖于一些经过实践验证的策略、方法和模式。这种观念对于软件工程领域的专业人士来说,已经司空见惯,设计模式已成为程序员们的重...【详细内容】
2024-03-27    InfoQ  Tags:架构模式   点击:(13)  评论:(0)  加入收藏
哈啰云原生架构落地实践
一、弹性伸缩技术实践1.全网容器化后一线研发的使用问题全网容器化后一线研发会面临一系列使用问题,包括时机、容量、效率和成本问题,弹性伸缩是云原生容器化后的必然技术选择...【详细内容】
2024-03-27  哈啰技术  微信公众号  Tags:架构   点击:(10)  评论:(0)  加入收藏
DDD 与 CQRS 才是黄金组合
在日常工作中,你是否也遇到过下面几种情况: 使用一个已有接口进行业务开发,上线后出现严重的性能问题,被老板当众质疑:“你为什么不使用缓存接口,这个接口全部走数据库,这怎么能扛...【详细内容】
2024-03-27  dbaplus社群    Tags:DDD   点击:(12)  评论:(0)  加入收藏
高并发架构设计(三大利器:缓存、限流和降级)
软件系统有三个追求:高性能、高并发、高可用,俗称三高。本篇讨论高并发,从高并发是什么到高并发应对的策略、缓存、限流、降级等。引言1.高并发背景互联网行业迅速发展,用户量剧...【详细内容】
2024-03-13    阿里云开发者  Tags:高并发   点击:(6)  评论:(0)  加入收藏
如何判断架构设计的优劣?
架构设计的基本准则是非常重要的,它们指导着我们如何构建可靠、可维护、可测试的系统。下面是这些准则的转换表达方式:简单即美(KISS):KISS原则的核心思想是保持简单。在设计系统...【详细内容】
2024-02-20  二进制跳动  微信公众号  Tags:架构设计   点击:(36)  评论:(0)  加入收藏
详解基于SpringBoot的WebSocket应用开发
在现代Web应用中,实时交互和数据推送的需求日益增长。WebSocket协议作为一种全双工通信协议,允许服务端与客户端之间建立持久性的连接,实现实时、双向的数据传输,极大地提升了用...【详细内容】
2024-01-30  ijunfu  今日头条  Tags:SpringBoot   点击:(19)  评论:(0)  加入收藏
PHP+Go 开发仿简书,实战高并发高可用微服务架构
来百度APP畅享高清图片//下栽のke:chaoxingit.com/2105/PHP和Go语言结合,可以开发出高效且稳定的仿简书应用。在实现高并发和高可用微服务架构时,我们可以采用一些关键技术。首...【详细内容】
2024-01-14  547蓝色星球    Tags:架构   点击:(115)  评论:(0)  加入收藏
GraalVM与Spring Boot 3.0:加速应用性能的完美融合
在2023年,SpringBoot3.0的发布标志着Spring框架对GraalVM的全面支持,这一支持是对Spring技术栈的重要补充。GraalVM是一个高性能的多语言虚拟机,它提供了Ahead-of-Time(AOT)编...【详细内容】
2024-01-11    王建立  Tags:Spring Boot   点击:(124)  评论:(0)  加入收藏
Spring Boot虚拟线程的性能还不如Webflux?
早上看到一篇关于Spring Boot虚拟线程和Webflux性能对比的文章,觉得还不错。内容较长,抓重点给大家介绍一下这篇文章的核心内容,方便大家快速阅读。测试场景作者采用了一个尽可...【详细内容】
2024-01-10  互联网架构小马哥    Tags:Spring Boot   点击:(119)  评论:(0)  加入收藏
站内最新
站内热门
站内头条