问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

IM系统分层架构深度解析:从终端层到存储层的完整架构设计

创作时间:
作者:
@小白创作中心

IM系统分层架构深度解析:从终端层到存储层的完整架构设计

引用
CSDN
1.
https://m.blog.csdn.net/wang_zong_sheng/article/details/143816967

随着互联网应用的不断发展,即时通讯(IM)系统已经成为现代通信的重要组成部分。在面对大规模用户和高并发场景时,传统的单体架构已经难以满足需求。本文将深入探讨IM系统的分层架构设计,从终端层到存储层,详细解析每个层次的功能和实现原理。

在日活只有几千的时候,IM系统采用【单体架构】方式进行实现,是完全没有问题的。在单体架构IM系统(见下图)这种方式下,如果用户的规模逐步增大,比如几万日活、十几万日活的时候,通常应该如何应对呢?

大家应该会很容易想到,不断横向扩展server节点即可,是的,这种方式会非常快捷和有效;但是,随着server节点数量的不断增多,大家会感觉到投入和产出的ROI越来越低,为什么会这样呢?

横向扩展server节点,其实是一种粒度非常粗的解决方案。server程序在实现的时候,一般会包含用于前端连接的部分、实现业务逻辑的部分和访问数据库与缓存的部分,这三部分到底是哪一部分的资源紧张才需要横向扩展server节点呢?日活量增大,有可能是前端连接逻辑需要扩容,也有可能是业务逻辑需要扩容,但是在横向扩展server节点时,通常不会考虑这么细。这是单体架构系统一个非常关键的问题。

另外,单体架构系统的技术栈非常单一,只能垂直扩展;举个例子:假设单体架构的系统最初选型用C++语言进行实现,前端连接的部分如果想改用SpringMVC进行实现,肯定不行的,访问数据库和缓存的部分如果想改用MyBatis进行实现,也肯定不行;没办法,只能在C++这个技术栈范围内死磕到底。

同时,单体架构的系统,随着业务迭代越多,其逻辑实现会愈加臃肿;任何一个小地方的改动,都会导致整个系统重新编译和重新部署。

所以,在大家遇到以上问题时,通常会采用【水平分层架构】进行实现。水平分层架构是在单体架构的基础上横向切几刀形成,是在技术的驱动之下,由单体架构演化而来;分层架构的IM系统见下图。

分层架构的IM系统,整体上包含:【终端层】、【入口层】、【业务逻辑层】、【路由层】、【数据访问层】和【存储层】,该分层架构的IM系统运行在日活百万级别的电商场景下;下面分层介绍。

一、终端层

终端层负责与用户进行交互,获取用户的操作行为,转换成访问后端具体接口的动作;终端层具体包括安卓和IOS移动手机上运行的客户端,即APP。

二、入口层

入口层,也叫做入口网关层,即Entry进程,Entry的职责只有一条,即负责维护与终端之间的长连接,不负责任何的业务逻辑。为什么要这样设计呢?

我们必须保证Entry的实现足够简单,简单到“一次实现,终生不迭代”,也就是Entry实现完进行部署之后,可以做到100年不重启;Entry是负责维护与客户端之间的长连接的,Entry如果重启,将会影响连接该Entry节点的所有的客户端。

对长连接的维护,通常采用“心跳”方式;也就是,对于客户端发送的所有请求中,Entry只负责处理心跳请求,其他请求Entry会全部转发给业务逻辑层进行处理。

三、业务逻辑层

业务逻辑层,即Logic进程,其负责处理IM系统的所有的业务逻辑,比如:登录、点对点消息、群消息、联系人等;可想而知,Logic进程是因为业务升级迭代,重启最频繁的。

四、路由层

路由层,即Router进程,通过名称可以很容易明白它的核心职责是实现用户的路由,怎么理解呢?我们已经知道Entry负责和客户端建立长连接,为了提高IM系统的吞吐量和避免单点问题,Entry肯定是集群化部署的;客户端与后端建立连接时,会根据策略将客户端分配给特定的Entry节点;Entry节点之间如果通信,代价太大,所以哪一个客户端由哪一个Entry节点来管理,必须要有一个中央存储来负责维护,该中共存储即是Router。

Router是一个内存数据库,其本质是一个很大的Map<uid, EntryIp>,该Map的key是用户的uid,该Map的value是客户端所连接到的Entry的ip;当IM系统要主动向用户推送消息时,可以先查询Router,如果用户记录不存在,说明该用户不在线;如果用户在线,获取到用户连接到了哪一个Entry节点,然后由Logic将消息推送到该Entry节点。

描述到这里,Entry的本质也是一个很大的Map<uid, fd>,该Map的key是用户的uid,该Map的value是长连接的文件描述符fd;通过查找该Map,获取到用户对应的socket的fd,然后基于fd将消息推送给用户。

通过思考路由层和入口层的本质,我们基本可以想清楚完整的消息推送流程的主要框架(后面的技术短文中会详细分析)。

五、数据访问层

数据访问层,即Das进程,该层的核心职责即负责访问数据库和缓存。理解该层的核心职责,不是最关键的,关键问题是数据访问层的架构意义何在呢?是不是说,在分层架构系统中,一定需要数据访问层,没有该层整个系统就不能干活了?答案肯定是否定的。

数据访问层是在“业务逻辑层”和“存储层”之间,大家在设计任何系统的时候都需要注意这种情况,两层之间若还有一层,那么该层的架构意义往往就是起到“解耦”的作用,也就是:数据访问层的存在是为了解耦业务逻辑层和存储层。具体怎么理解呢?

我们都知道,存储层是非常复杂的:为了提高抗读的压力,我们会加一个“缓存”;如果单表的数据量超过限制,我们会“分库分表”;为了进一步优化读库,我们会对数据库进行“读写分离”;我们也有可能会将MongoDB替换成MySQL,将MySQL替换成TiDB等等等等。存储层这一系列的复杂性,如果没有数据访问层的存在,势必会影响到业务逻辑层,而业务逻辑层是进行业务迭代的,如果我们正在进行“分库分表”的操作,还有精力迭代业务吗?但是如果有了数据访问层,它统统屏蔽了存储层的复杂性,业务逻辑层只管向数据访问层要数据即可,不用关注存储层的数据存储模式。这就是数据访问层的架构意义。

六、存储层

存储层负责对业务数据进行持久化存储,通常包括数据库和缓存。

最后,总结文中关键:

  1. 单体架构系统,横向扩展server节点方案,粒度很粗,ROI很低;
  2. 分层架构IM系统,包括【终端层】、【入口层】、【业务逻辑层】、【路由层】、【数据访问层】和【存储层】;
  3. 入口层Entry只负责维护与终端之间的长连接;
  4. 路由层Router是一个内存数据库,是用户在线状态的中央存储,其本质是一个很大的Map<uid, EntryIp>;Entry的本质也是一个很大的Map<uid, fd>;
  5. 数据访问层Das存在的架构意义在于向业务逻辑层屏蔽存储层的复杂性,解耦业务逻辑层和存储层。

大家思考一下:

分层架构的IM系统,在业务逻辑越来越复杂,为了引流而经常进行各种运营活动时,业务逻辑层需要进行怎样的调整呢?

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号