开源OpenIM:高性能、可伸缩、易扩展的即时通讯架构

本文涉及的产品
云数据库 MongoDB,独享型 2核8GB
推荐场景:
构建全方位客户视图
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
简介: 网上有很多关于IM的教程和技术博文,有亿级用户的IM架构,有各种浅谈原创自研IM架构,也有微信技术团队分享的技术文章,有些开发者想根据这些资料自研IM。理想很丰满,现实很骨感,最后做出来的产品很难达到商用标准。事实上,很多架构没有经过海量用户的考验,当然我们也不会评判某种架构的好坏,如果开发者企图根据网上教程做出一个商用的IM,可能有点过于乐观了。本文主要从我个人角度深度剖析100%开源的OpenIM架构。当然,世界上没有最完美的架构,只有最合适的架构,也没有所谓的通用方案,不同的解决方案都有其优缺点,只有最满足业务的系统才是一个好的系统。而且,在有限的人力、物力,综合考虑时间成本,通常需要做

IM系统技术挑战

可靠性

IM消息系统的可靠性,通常就是指消息投递的可靠性,即我们经常听到的“消息必达”,通常用消息的不丢失和不重复两个技术指标来表示。确保消息被发送后,能被接收者收到。由于网络环境的复杂性,以及用户在线的不确定性,消息的可靠性(不丢失、不重复)无疑是IM系统的核心指标,也是IM系统实现中的难点之一。总体来说,IM系统的消息“可靠性”,通常就是指聊天消息投递的可靠性(准确的说,这个“消息”是广义的,因为还存用户看不见的各种指令和通知,包括但不限于进群退群通知、好友添加通知等,为了方便描述,统称“消息”)。

从消息发送者和接收者用户行为来讲,消息“可靠性”应该分为以下几种情况:

(1)发送失败,对于这种情况IM系统必须要感知到,明确反馈发送方。如果此消息没有发送成功,发送方可以选择重试或者稍后再试。

(2)发送成功,如果接收方处在“在线”状态,应该立即收到此消息。如果接收方处在“离线”状态不能收到消息,一旦上线则立刻收到消息。

(3)消息不能重复,用数学术语表示:“有且仅有这条消息”,如果重复了,可能表达的意思就变了。总之,一个商用 IM系统,必须包含消息“可靠性”逻辑,才能谈基本可用,这是IM系统最基本也是最核心的逻辑。

有序性(一致性)

IM系统中,特别需要考虑消息时序问题,如果后发送的消息先显示,可能严重扰乱聊天消息所要表达的意义,会造成聊天语义不连贯,引起误会。消息的时序性,也称为消息收发一致性,主要目标是:保证聊天消息的绝对时序。IM系统中消息时序的一致性问题看似简单,实则是非常有难度的技术热点话题之一。为什么会出现时序问题  1、分布式系统的出现导致时序不一致。IM系统模块众多,接入层、消息逻辑层等、每层都分布式集群化,这些应用分布在不同的机器上,如何保证时序是个难点。2、网络传输延迟导致时序不一致。不同用户发送的消息到达服务器的延时差异较大,给消息时序性带来挑战。

消息时序是分布式系统架构设计中非常难的问题,一个分布式的IM系统必须要解决这个问题,如何高效、低成本解决这个问题,是我们OpenIM要考虑的方向。

实时性

实时性,即消息实时到达接收方,如果用户在线,则实时可达,如果用户不在线,则登录时可达。由于网络波动,以及移动端操作系统对应用前后台切换的管理,如何实现用户连接管理、消息实时推送,推送失败的处理方式,客户端重连机制,消息如何补齐等,都是需要IM系统考虑,同时要结合移动端的特点,兼顾耗电量,网络,性能等。由于TCP开发略微复杂,早期的基于HTTP短轮询、长轮询的低效的技术方案,也无法达到实时性的要求。

扩展性

一般来说互联网系统的扩展性包含多个含义,我们侧重讲解关于IM消息的扩展性。IM业务特性多,功能丰富,从聊天类型来看,分为:单聊、群聊,聊天室等;从消息类型来看,分为:文本、图片、视频、地理位置、自定义消息等;从消息功能来看,分为:撤回、在线状态、对方正在输入、阅后即焚等;从通知角度来看,分为:进群、退群、添加好友、验证好友等各种通知。如何有效支撑、扩展功能,高效实现,是考验IM扩展性的一个方面,也是对系统架构设计能力的考验。为了更好地提高数据通道对业务支撑的扩展性,我们首创了“一切皆消息”的消息模型,即通讯双方产生的所有消息、通知,服务端以消息统一处理,扮演了消息通道的角色,客户端针对不同消息类型做不同的UI展示,完美解决了扩展性问题。

IM系统术语以及本文档专有名词解释

conversationId:会话Id,会话是指用户和用户之间,以及用户和群之间,进行通讯后产生的关联。

userId:用户Id:注册使用IM的用户Id,从消息的发送和接收来看有两个身份:发送者和接收者

sendId:消息发送者Id

receiverId :消息接收者Id

msg:消息是指用户之间的沟通内容,一般指用户主动产生的。同时也包括用户看不见的各种指令和通知,包括但不限于进群退群通知、好友添加通知等

inbox:用户收件箱,给某人发送消息,实际上是往接收者“信箱”写入消息,这个信箱就是收件箱

seq:用户收件箱中消息序列号,分为local seq,和server seq,前者表示app本地消息seq,后者表示服务端消息seq,seq是连续且递增的。

conn:登录用户的连接信息,用于消息推送;

MQ:消息队列,一般用来解决应用解耦,异步消息,流量削峰等问题,实现高性能,高可用,可伸缩和最终一致性架构,本文采用kafka组件。

OpenIM的诞生

随着移动互联网的蓬勃发展, IM 作为一种通讯能力,已经成为互联网上的基础设施,也是许多 APP 不可或缺的功能。如何让每一个应用都具备IM功能,同时考虑企业的接入成本、服务器资源以及最重要的数据安全性和私密性。本人从微信离职后,创办了开源OpenIM,是全球首家100%开源、免费项目,并提供IMSDK,覆盖所有主流开发平台,iOS、Android、Flutter、react native、Windows、Linux、Unity、web、小程序等。

开源IM现状

github 上 IM 开源项目不少,但开发者却难以使用,主要有几点原因(1)个人项目居多,但近几年都无人维护,遇到问题无人解决,企业商业化产品不敢冒险使用(2)大部分项目不是 IM 技术专业团队完成的,技术实力和技术架构存疑,也没有经过大项目和海量用户检验;(3)只开源服务端或者客户端,只开源某一端,需要开发者实现另外一端,研发成本同样不小,另外,开源项目大部分都是以聊天app形式开源,开发者如何把 IM 集成到自身 app 中,同样存在大量的修改和适配成本。(4)部分项目打着开源的旗号,社区版免费,但核心功能缺失,商业版收费。

云服务商的弊端

IM 云服务商提供 IM SDK 和 API ,让开发者简单集成 IM 功能,当然这里也存在明显的问题(1)成本问题:企业每年额外支付上万乃至数十万的云服务费用,从长期来看是个不小的成本;(2)数据隐私问题:企业的用户数据、聊天记录等核心数据托管在 IM 云服务商,如何保证客户的数据隐私和安全性;(3)需求定制问题:IM 需求多样化,IM 功能只能由 IM 云服务商通过 SDK 的形式提供给大家使用,开发受限,所有功能都需要封装成接口;(4)捆绑问题:一旦使用 IM 云服务,形成捆绑关系,迁移成本高,受制于人。

自研的尴尬

IM 是一个看起来门槛很低的项目,网上有很多所谓的 IM 开发教程,甚至很多毕业设计也是做一个 IM 系统。由于这个误区的存在,很多企业盲目乐观组建 3-5 人的 IM 团队,历时一年半载,最后只完成了一个 demo 版本。由于架构设计不合理,demo 版本存在消息丢失、系统异常等 bug,无法达到商用的要求。IM系统除了面临互联网业务系统本身的挑战,还存在上文分析的可靠性、时序性、扩展性等问题,所以,自研IM,对于中小企业来说,可能是最糟糕的选择。

OpenIM的整体架构

后台架构设计.png

OpenIM分为两大块

(一)Open-IM-SDK-Core  采用golang实现客户端逻辑,主要负责本地db存储及更新;断网重连及管理;消息及各种通知回调。本地消息、会话等数据存储,通过通知机制完成本地数据实时同步,同时兼顾客户端缓存的作用,有效缓解了服务端压力。另外,golang跨平台的特性,使得各移动平台都能无缝调用,开发者只需根据产品需求编写UI界面,通过回调机制和SDK完成数据交互和通知。

(二)Open-IM-Server 由接入层、逻辑层和存储层组成,好处在于各层能够依据业务特点专注于自己的事情,提高系统复用性,降低业务间的耦合。

(1)接入层:消息通过 websocket 协议接入,其他业务通过 http/https 协议提供REST API实现。消息是高频及核心功能,通过双协议路由,体现了轻重分离的设计思想。

(2)逻辑层:通过 rpc 实现无状态逻辑服务,易于平行扩展,模块通过 MQ 解耦。

(3)存储层:redis 存储 token 和 seq;mongodb 存储离线消息,并定时删除 14 天内(可自行配置)数据;mysql 存储全量历史消息以及用户相关资料。数据分层存储,充分利用不同存储组件的特性。

(4)Etcd:服务注册和发现、以及分布式配置中心。

消息网关msg_gateway

消息接入层,采用websocket协议接入,import gorilla具体实现,服务模块无状态,柔性伸缩,运维简单。通过MQ让业务模块之间解耦,消息写入MQ即表示发送成功。

(1)负责用户连接管理,保持长连接,存储uid->conn映射关系;

(2)负责消息接收落地,成功写入MQ后给客户端返回成功;

(3)负责把消息推送给在线状态的接收者;

下图是客户端发送消息流程

消息发送时序图.png

消息转发msg_transfer

消息处理rpc,作为消费者从MQ中消费(读取)消息,递增接收者收件箱seq,关联seq和msg,并存储到mongodb。全量历史消息无收件箱概念,消息作为流水记录落地mysql即可,两者通过协程独立处理,双方互不影响。msg作为无状态服务节点,如果消息量增加,可以启动冗余节点服务,加快消息处理流程。

(1)负责消费MQ中的消息,作为消费者,实时感知新信息达到,并触发回调逻辑;

(2)生成msgId作为全局消息Id;

(3)读取receiver userId,并通过redis的incr操作递增服务端对应的seq;

(4)关联seq和msgid,并存入以receiver userid为key的mongodb中,作为离线消息,一般在14天后会删除;

(5)同时,把消息作为历史记录存入mysql中,作为消息备份,或其他用途。

(4)和(5)是两个独立的协程并行执行的,mysql写入快慢不会影响mongodb的写入,这样既完成了冷热数据分离,也充分利用了机器资源。

下图是消息处理入库流程

消息存储时序图.png

消息推送push

msg_transfer完成存储消息到后,向push发起消息推送任务,msg_gateway查询本地userId->conn表,如果用户在线则推送给接收者,对于msg_gateway的推送架构设计,做成了“半状态”服务,即在节点本地存储了用户连接信息,作为局部信息,没有通过redis全局共享。push推送消息时,向所有msg_gateway发送推送请求,带来一定的“惊群效应”,由于msg_gateway节点不多,所以影响有限,带来的好处则是在不影响性能的前提下,msg_gateway设计和实现简单,运维也更简单。

(1)msg_transfer把消息写入mongodb后,发送push消息推送请求;

(2)push提供rpc推送服务,通过etcd找到所有注册的msg_gateway,并发送推送请求;

(3)msg_gateway从本节点内存中查询userId->conn,如果找到conn,则向客户端推送消息;

(4)如果消息接收者不在线,msg_gateway无法推送消息,但客户端网络重连时会及时同步历史消息,进行消息补齐;

下图是消息实时推送流程:

消息推送时序图.png

消息同步及对齐seq

由于网络的波动以及负责的网络环境,导致消息推送存在不确定性。OpenIM采用local seq和server seq消息对齐,同时结合拉取和推送的方式,简单高效地解决了消息的可靠性问题。这里分两种场景进行表述:

(1)客户端接收推送消息时,比如客户端收到推送消息的seq为100,如果local seq为99,因为seq递增且连续,所以消息正常显示即可。如果local seq大于100,说明重复推送了消息,抛弃此消息即可。如果local seq小于99,说明中间有历史消息丢失,拉取(local seq+1, 100)的消息,进行补齐即可;

(2)用户在登录、或者断网重连时,客户端会从服务端拉取最大seq(max seq),读取客户端本地seq(local seq),如果local seq 小于 max seq,说明存在历史消息未同步的情况,调用接口同步自身收件箱[local seq+1, max seq]的数据完成消息对齐。

下图是消息同步流程图

消息拉取时序图.png

本文主要简单阐述了OpenIM的架构以及消息流程,让开发者对其有初步认识,在接下来的文章中,我们会详细讲解OpenIM服务端消息架构,OpenIM客户端架构,同时会详细分析OpenIM如何简单高效解决消息的可靠性、实时性、一致性和扩展性问题。


更多阅读

基于Tablestore Timeline的IM(即时通讯)消息系统架构 - 架构篇

OpenIM官网

相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。   相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
目录
相关文章
|
19天前
|
监控 持续交付 API
深入理解微服务架构:构建高效、可扩展的系统
【10月更文挑战第14天】深入理解微服务架构:构建高效、可扩展的系统
69 0
|
4天前
|
存储 SQL Apache
Apache Doris 开源最顶级基于MPP架构的高性能实时分析数据库
Apache Doris 是一个基于 MPP 架构的高性能实时分析数据库,以其极高的速度和易用性著称。它支持高并发点查询和复杂分析场景,适用于报表分析、即席查询、数据仓库和数据湖查询加速等。最新发布的 2.0.2 版本在性能、稳定性和多租户支持方面有显著提升。社区活跃,已广泛应用于电商、广告、用户行为分析等领域。
Apache Doris 开源最顶级基于MPP架构的高性能实时分析数据库
|
3天前
|
监控 前端开发 JavaScript
探索微前端架构:构建可扩展的现代Web应用
【10月更文挑战第29天】本文探讨了微前端架构的核心概念、优势及实施策略,通过将大型前端应用拆分为多个独立的微应用,提高开发效率、增强可维护性,并支持灵活的技术选型。实际案例包括Spotify和Zalando的成功应用。
|
7天前
|
运维 Serverless 数据处理
Serverless架构通过提供更快的研发交付速度、降低成本、简化运维、优化资源利用、提供自动扩展能力、支持实时数据处理和快速原型开发等优势,为图像处理等计算密集型应用提供了一个高效、灵活且成本效益高的解决方案。
Serverless架构通过提供更快的研发交付速度、降低成本、简化运维、优化资源利用、提供自动扩展能力、支持实时数据处理和快速原型开发等优势,为图像处理等计算密集型应用提供了一个高效、灵活且成本效益高的解决方案。
31 1
|
10天前
|
编解码 人工智能 开发者
长短大小样样精通!原始分辨率、超长视频输入:更灵活的全开源多模态架构Oryx
【10月更文挑战第23天】Oryx 是一种新型多模态架构,能够灵活处理各种分辨率的图像和视频数据。其核心创新在于能够对图像和视频进行任意分辨率编码,并通过动态压缩器模块提高处理效率。Oryx 在处理长视觉上下文(如视频)时表现出色,同时在图像、视频和3D多模态理解方面也展现了强大能力。该模型的开源性质为多模态研究社区提供了宝贵资源,但同时也面临一些挑战,如选择合适的分辨率和压缩率以及计算资源的需求。
21 3
|
19天前
|
运维 监控 Serverless
利用Serverless架构优化成本和可伸缩性
【10月更文挑战第13天】Serverless架构让开发者无需管理服务器即可构建和运行应用,实现成本优化与自动扩展。本文介绍其工作原理、核心优势及实施步骤,探讨在Web应用后端、数据处理等领域的应用,并分享实战技巧。
|
22天前
|
运维 Serverless 数据处理
Serverless架构通过提供更快的研发交付速度、降低成本、简化运维、优化资源利用、提供自动扩展能力、支持实时数据处理和快速原型开发等优势,为图像处理等计算密集型应用提供了一个高效、灵活且成本效益高的解决方案。
Serverless架构通过提供更快的研发交付速度、降低成本、简化运维、优化资源利用、提供自动扩展能力、支持实时数据处理和快速原型开发等优势,为图像处理等计算密集型应用提供了一个高效、灵活且成本效益高的解决方案。
54 3
|
6天前
|
机器学习/深度学习 人工智能 自然语言处理
Tokenformer:基于参数标记化的高效可扩展Transformer架构
本文是对发表于arXiv的论文 "TOKENFORMER: RETHINKING TRANSFORMER SCALING WITH TOKENIZED MODEL PARAMETERS" 的深入解读与扩展分析。主要探讨了一种革新性的Transformer架构设计方案,该方案通过参数标记化实现了模型的高效扩展和计算优化。
36 0
|
25天前
|
消息中间件 存储 监控
探索微服务架构:构建可扩展的应用程序
【10月更文挑战第8天】探索微服务架构:构建可扩展的应用程序
28 0
|
6天前
|
弹性计算 Kubernetes Cloud Native
云原生架构下的微服务设计原则与实践####
本文深入探讨了在云原生环境中,微服务架构的设计原则、关键技术及实践案例。通过剖析传统单体架构面临的挑战,引出微服务作为解决方案的优势,并详细阐述了微服务设计的几大核心原则:单一职责、独立部署、弹性伸缩和服务自治。文章还介绍了容器化技术、Kubernetes等云原生工具如何助力微服务的高效实施,并通过一个实际项目案例,展示了从服务拆分到持续集成/持续部署(CI/CD)流程的完整实现路径,为读者提供了宝贵的实践经验和启发。 ####

热门文章

最新文章