本文由冰河分享,由博客冰河撰写gitcode.主持人,原来的问题“如何在简历上写这个分布式IM即时通讯系统?我已经帮你整理好了!本文档已经过修订和修改。分布式IM即时通讯系统本质上是对在线聊天和用户的管理。
对于聊天本身,核心要求是:发送文本、文件、语音、消息缓存、消息存储、消息未读、已读、召回、离线消息、历史消息、一对一聊天、群聊、多端同步等需求。
对于用户管理,现有要求包括:添加好友、查看好友列表、删除好友、查看好友信息、创建群聊、加入群聊、查看群成员信息、退出群聊、修改群昵称、拉人**、踢出群、解散群聊、填写群公告、修改群备注等用户相关需求。
为了更好地理解分布式IM即时通讯系统的设计,我站在架构师的角度,在充分了解了系统需求、业务流程和技术流程之后,从全局的角度为系统设定了方案目标,选择了技术方案,设计了系统的整体架构和分层架构, 并整理出发送消息、单聊、群聊的互动链接。希望它有所帮助。
技术交流:
- 关于移动 IM 开发的介绍性文章:“对于初学者来说,一篇文章就足够了:从头开始开发移动 IM”。(本文也发表在。开源 IM 框架源代码:备用地址单击此处)。
在技术选型和整体架构设计之前,需要明确一件事,那就是无论系统采用哪种方案,采用哪种架构设计,都需要明确方案的业务目标、技术目标和架构目标,在研发过程中要不断评估系统的整体性能, 并应找到系统瓶颈并不断优化。
一般来说,我们构建和开发的分布式IM即时通讯系统需要满足以下计划目标。
具体说来:
业务目标:满足需求设计章节中的各种需求场景;
技术目标:支持无限扩展,百万用户同时扩容
架构目标:高并发、高性能、高可用、监控、预警、扩展、无限扩展。
在技术选择上,除了使用springboot等基础框架外,还会使用容器化方案。
同时,为了尽可能降低技术门槛,整个分布式IM即时通讯系统的技术选型主要采用市面上比较流行的技术框架和解决方案。
具体选型如下:
开发框架:SpringBoot、SpringCloud、SpringCloud 阿里巴巴、Dubbo
缓存:Redis分布式缓存+gu**a本地缓存;
数据库:MySQL、TiDB、HBase;
流量网关:openresty+lua;
服务网关:SpringCloud Gateway + Sentinel
持久化层框架:mybatis、mybatis-plus;
服务配置、服务注册和发现:nacos;
消息中间件:rocketmq;
网络通信:netty;
文件存储:minio;
日志可视化治理:麋鹿;
集装箱化管理:蜂群、猪笼;
监控:Prometheus、Grafana;
前端:vue;
单元测试:junit;
基准:jmh;
压力测试:jmeter。
对于IM即时通讯系统,涵盖即时通讯后端服务、大型后端平台、SDK接入服务、OpenAI接入服务、大型前端UI,相信很多小伙伴或多或少都能绘制出IM即时通讯系统的架构图,大致如下图所示。
其实这种架构设计也是比较常见的,在这种架构设计中,Kong OpenResty NGINX 只做负载均衡和反向**,研发人员更注重业务层和基础层的开发,流量比较小,这种架构设计一般不会有任何问题。 但是,一旦流量比较大,当用户调用后端平台的接口发送消息时,即时通讯SDK同步调用即时通讯服务的接口,就会造成性能问题。
由于每个终端只能同时与一个 IM 即时通讯服务实例建立连接,因此,如果一个 IM 即时通讯服务恰好连接了大量用户终端,则即时通讯 SDK 会频繁调用同一 IM 即时通讯服务的接口,从而出现性能瓶颈。 在这种情况下,当性能瓶颈出现时,不仅会影响IM即时通讯服务,还会影响后端平台接收请求的业务。
既然上图所示的架构设计存在性能瓶颈,我们该如何优化呢?
为此,我们在上图的基础上对架构进行了优化,优化后的架构如下图所示。
对比两张图可以看出,在屏蔽技术实现细节的前提下,我们将前端对业务的验证和流量控制,并放大 Kong OpenResty NGINX 的职责,让这些软件不仅具备反向**和负载均衡的功能,还实现了限速的功能, 黑白名单、流量控制、服务验证。
也就是说,在这种架构模式下,我们充分发挥了整个分布式IM即时通讯系统的入口责任,充分利用了Kong OpenResty NGINX的高并发、高吞吐能力,并尝试拦截了整个系统的大部分无效请求。 例如,在不登录系统的情况下,用户尝试调用发送消息、添加好友、添加**组等接口。 这将大大减轻后端平台的业务压力。
除了在 Kong OpenResty NGINX 中实现限速、黑白名单、流量控制、服务验证等功能外,我们还引入了服务网关集群,实现限速、降级、熔断、流量控制、验证、认证等功能,进一步保障下游系统的稳定性和安全性。
为了解决大量用户终端恰好连接到同一个 IM 即时消息服务实例导致的性能问题,IM 即时消息 SDK 频繁调用同一个 IM 即时消息服务实例的接口。 我们在 IM 即时通讯服务 SDK 和 IM 即时通讯服务之间引入了一个 RocketMQ 集群。
IM 集群中的每个 IM 实例在集群中都有一个唯一的 ID,每个 IM 实例在启动后只监听 RocketMQ 中与其 ID 相关的主题。 这样一来,每个 IM 即时通讯服务将只接收主题中与自己 ID 相关的消息,而不会接收到所有消息。
当用户登录系统时,会与IM即时通讯服务建立持久连接,用户ID和终端作为密钥,IM即时通讯服务ID作为值,存储在分布式缓存中。 同时,将用户ID和终端作为密钥,以用户终端与IM即时通讯服务之间的持久连接为值,IM即时通讯服务的本地内存将存储在本地内存中。
当用户调用后端平台接口发送消息时,会随身携带目标用户的ID,并在IM即时通讯SDK中指定用户登录的终端设备,最后通过IM即时通讯SDK将消息发送到RocketMQ。
这种情况下,IM SDK基于目标用户ID和终端,从分布式缓存中获取目标用户连接的IM即时通讯服务的ID,并向该ID相关的主题发送消息。 这种情况下,与目标用户建立持久连接的 IM 即时消息服务接收到 RocketMQ 中的消息,然后根据用户 ID 和终端从本地缓存中获取与用户终端建立的持久连接,并基于持久连接向用户推送消息。
此外,在实际实现中,为了防止大量用户同时只连接到IM即时通讯服务集群中的一个服务实例,用户会对IP、浏览器指纹、手机设备等进行哈希和模运算,使其尽可能均匀地分布到集群中的每个服务实例。
那么问题来了,这个架构设计还有进一步优化的空间吗?
为了进一步提升分布式IM即时通讯系统的性能、可用性和弹性扩展性,我们可以设计分布式IM即时通讯系统的容器化架构,如下图所示。
可以看出,我们进一步优化了分布式IM即时通讯系统的架构设计,采用了容器化的架构设计。 在原有架构的基础上,我们做了如下的改进和优化。
1) 基本支持服务:基础支持服务将通过各种基础中间件、数据存储服务、监控服务实现,包括:MySQL数据库、Tidb数据库、HBase、Redis缓存、RocketMQ消息队列、Prometheus监控、Portainer容器管理等基础中间件实现,基础支持服务将为整个分布式IM即时通讯系统提供最基础的数据、传输、监控和容器管理服务。
2)集装箱化:在容器化层面,它将通过 Docker、Swarm 和 Portainer 实现,其中容器化将基于 Swarm 和 Portainer 进行管理。
3)其他基本功能实现:除了上述分层架构外,对于分布式IM即时通讯系统的构建,还需要考虑异常监控、服务注册与发现、可视化、业务降级与数据恢复、业务速率限制、业务容灾恢复、容量规划与伸缩、全链路压力测试等。
在分布式IM即时通讯系统中,无论是大型后端平台还是IM即时通讯服务,我们都会对业务层采用分层的业务架构。
在这里,我们可以借鉴DDD的分层架构思想,将其分为四个层:显示层、应用层、领域层和基础设施层。
但是,考虑到分布式IM即时通讯系统的特殊性,不会严格按照DDD的原则进行设计,如下图所示。
可以看出,分布式IM即时通讯系统会借鉴DDD的设计思想,但不会完全按照DDD的方式进行设计。
1)显示层:表示层,也称为用户UI层,是DDD设计的顶层,提供API接口,接收客户端请求,解析参数,返回结果数据,处理异常。
2)应用层:应用层又称应用层,主要处理容易发生变化的业务场景,可以处理相关事件、调度等聚合操作。
3) 域层:领域层,又称领域层,可以说是DDD设计的本质,就是将业务系统中相对不变的部分抽象出来,封装成一个领域模型。 在分布式IM即时通讯系统的设计中,域层基本不依赖于其他层,也不依赖于基础设施层,这与DDD设计不同。
4)基础设施层:基础结构层(也称为基础结构层)为其他层提供通用的基本功能,包括分布式 IM 即时消息系统中的缓存、常规实用程序类、消息和系统持久性机制。
在分布式IM即时通讯系统中,我们忽略了其他一些细节,而把重点放在了发送消息的交互链接逻辑上。 无论是单聊还是群聊,都需要通过IM即时通讯服务将消息推送到用户的终端。 此时发送消息的流程如下图所示。
可以看出:当用户在分布式IM即时通讯系统中发送消息时,无论是单聊还是群聊,最终的消息都会被推送到用户登录的终端设备。 假设用户 A 向用户 B 发送消息,或者用户 A 和用户 B 在同一个组,用户 A 向该组发送消息,则用户 B 接收消息的主要流程如下:
具体说来:
用户 A 调用后端平台的 API 向用户 B 发送消息,消息中会包含用户 B 的 ID 和终端信息
后端平台缓存消息并将其异步写入消息库
后端平台从REDIS获取用户B连接的IM即时通讯服务的ID
后端平台获取到用户 B 连接的 IM 消息服务 ID 后,向 RocketMQ 中用户 B 连接的 IM 消息服务 ID 对应的 topic 发送消息
IM 即时通讯服务监听 RocketMQ 中与其服务 ID 对应的主题的消息,用户 B 连接的 IM 即时通讯服务接收到该消息
IM即时通讯服务收到消息后,会根据用户B的ID和终端信息,从缓存中获取用户B与IM即时通讯服务之间的连接,并通过该连接将消息推送给用户B。
为了实现上述发送消息的过程,必须满足以下条件:
后端平台满足分发条件,可随时横向扩展
IM即时通讯服务满足分发条件,可随时横向扩展
每个启动的 IM 即时消息服务实例在群集中都有一个唯一的 ID
每个 IM 服务只监听 RocketMQ 中与其 ID 对应的主题的消息
用户登录分布式IM即时通讯系统后,会与IM即时通讯服务建立持久连接,根据用户ID和用户所在终端缓存持久连接,根据用户ID及其所在终端缓存已连接的IM即时通讯服务的ID到Redis中
用户发送消息时,会根据目标用户的 ID 和终端从 Redis 获取 IM 即时消息服务 ID,然后向当前 IM 即时消息服务 ID 对应的 RocketMQ 主题发送消息
对应的 IM 即时消息服务监听并接收到 RocketMQ 消息后,会根据用户的 ID 和终端,从缓存中获取用户的连接信息,并将消息推送给用户。
一对一聊天是分布式IM即时通讯系统中用户与另一个用户之间的一对一聊天。 在这种情况下,很有可能在单独聊天的两个用户中,该用户不是**。
例如:当用户 A 向用户 B 发送消息时,用户 B 可能不会 **。
此时,我们需要存储用户 A 发送给用户 B 的消息。
事实上,在分布式IM即时通讯系统中,我们意识到,无论用户B是否**,消息记录都会被存储。 当用户B登录系统时,消息会同步到用户B,如下图所示。
如您所见,当用户 A 向用户 B 发送消息时:
如果是用户 b**,则可以根据发送消息的交互链接向用户 b 发送消息
如果用户 B 没有 **,则无法正常向用户 B 推送消息。 用户 B 登录分布式 IM 即时通讯系统时,会调用后端平台的接口拉取所有未读消息,并通过用户 B ** 进程将消息推送给用户 B。
群聊是一种分布式IM即时通讯系统,其中多个用户在同一群中聊天。
这时,在发送消息时,我们可以通过群ID找出群内所有用户,并立即将消息发送给群内用户。
那些没有**的用户将被视为在单次聊天中没有**的用户,如下图所示。
如您所见,群聊的交互式链接流程如下:
用户调用后端平台接口向群组发送消息
后端平台缓存消息并将其异步写入消息库
由于您正在向组发送消息,并且该组中有多个用户,因此您将从 Redis 获得连接到所有用户的 IM 即时消息服务 ID 列表
用户按照服务ID分组,同一服务ID下的用户分组在同一个逻辑组中,方便后续推送消息,没有**的用户列表会被记录下来
消息循环发送到 RocketMQ 中每个服务 ID 对应的主题
广播处理未读用户的未读消息 ID **
IM 即时消息服务监听自己服务 ID 对应的主题,随时接收推送到自己服务的消息
当 IM 即时消息服务收到消息时,用户断开连接,或者用户没有**,向用户推送消息将失败,或者无法查询用户与 IM 即时消息服务之间的连接,并且不会向用户推送消息
当用户登录分布式IM即时通讯系统时,会从后端平台拉取历史(离线)消息,通过用户的进程将消息推送给用户
那么,看到这里,你明白如何设计一个高度可扩展的分布式IM即时通讯系统了吗?
1] *IM系统的架构设计。
2] 简要描述移动 IM 开发的陷阱:架构设计、通信协议和客户端。
3] 一套移动IM架构设计实践分享(含细节**)海量用户
4] 一套独创的分布式即时通讯(IM)系统理论架构方案。
5] 如何保证移动IM中大规模群消息推送的效率和实时性?
6] 一套面向亿用户的IM架构技术(上):整体架构、业务拆分等。
7】一套面向亿用户的IM架构技术干货(第二部分):可靠性、有序性、弱网优化等。
8] 从新手到专家:如何设计一个拥有数亿条消息的分布式 IM 系统。
9] 企业微信的IM架构设计揭秘:消息模型、万人、已读回执、消息撤回等。
10] 融云科技分享:全面揭开亿级IM消息可靠传递机制。
11] 阿里IM技术分享(3):仙宇亿级IM消息系统的架构演进。
12] 基于实践:总结了具有百万消息量的小型 IM 系统的技术要点。
13] 从源码学习 IM (10):基于 Netty 构建高性能 IM 集群(含技术思路 + 源码)
14] 一套10万级TPS IM集成消息系统的架构实践与思考。
15] 自主研发的客服IM系统从0到1的技术实践。
16] 面向海量用户的IM聊天室的架构设计与实践。
17]史上最火的netty入门长文:基础介绍、环境建设、动手实践。
18] 初学者介绍:迄今为止对 Netty 的高性能原则和框架架构进行了最彻底的分析。
19] 对于初学者:高性能蔚来框架Netty的习方法和高级策略。
20] 教你如何使用Netty实现网络通信程序的心跳机制和断线重连机制。
21] 史上最强的 J**a Nio:如果你担心开始和放弃,请阅读这篇文章!
(本文也发表在。