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

本文涉及的产品
云数据库 MongoDB,独享型 2核8GB
推荐场景:
构建全方位客户视图
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
简介: 网上有很多关于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
目录
相关文章
|
28天前
|
监控 持续交付 API
深入理解微服务架构:构建高效、可扩展的系统
【10月更文挑战第14天】深入理解微服务架构:构建高效、可扩展的系统
77 0
|
2天前
|
消息中间件 缓存 架构师
关于 Kafka 高性能架构,这篇说得最全面,建议收藏!
Kafka 是一个高吞吐量、高性能的消息中间件,关于 Kafka 高性能背后的实现,是大厂面试高频问题。本篇全面详解 Kafka 高性能背后的实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
关于 Kafka 高性能架构,这篇说得最全面,建议收藏!
|
13天前
|
存储 SQL Apache
Apache Doris 开源最顶级基于MPP架构的高性能实时分析数据库
Apache Doris 是一个基于 MPP 架构的高性能实时分析数据库,以其极高的速度和易用性著称。它支持高并发点查询和复杂分析场景,适用于报表分析、即席查询、数据仓库和数据湖查询加速等。最新发布的 2.0.2 版本在性能、稳定性和多租户支持方面有显著提升。社区活跃,已广泛应用于电商、广告、用户行为分析等领域。
Apache Doris 开源最顶级基于MPP架构的高性能实时分析数据库
|
12天前
|
监控 前端开发 JavaScript
探索微前端架构:构建可扩展的现代Web应用
【10月更文挑战第29天】本文探讨了微前端架构的核心概念、优势及实施策略,通过将大型前端应用拆分为多个独立的微应用,提高开发效率、增强可维护性,并支持灵活的技术选型。实际案例包括Spotify和Zalando的成功应用。
|
15天前
|
运维 Serverless 数据处理
Serverless架构通过提供更快的研发交付速度、降低成本、简化运维、优化资源利用、提供自动扩展能力、支持实时数据处理和快速原型开发等优势,为图像处理等计算密集型应用提供了一个高效、灵活且成本效益高的解决方案。
Serverless架构通过提供更快的研发交付速度、降低成本、简化运维、优化资源利用、提供自动扩展能力、支持实时数据处理和快速原型开发等优势,为图像处理等计算密集型应用提供了一个高效、灵活且成本效益高的解决方案。
51 1
|
18天前
|
编解码 人工智能 开发者
长短大小样样精通!原始分辨率、超长视频输入:更灵活的全开源多模态架构Oryx
【10月更文挑战第23天】Oryx 是一种新型多模态架构,能够灵活处理各种分辨率的图像和视频数据。其核心创新在于能够对图像和视频进行任意分辨率编码,并通过动态压缩器模块提高处理效率。Oryx 在处理长视觉上下文(如视频)时表现出色,同时在图像、视频和3D多模态理解方面也展现了强大能力。该模型的开源性质为多模态研究社区提供了宝贵资源,但同时也面临一些挑战,如选择合适的分辨率和压缩率以及计算资源的需求。
27 3
|
27天前
|
运维 监控 Serverless
利用Serverless架构优化成本和可伸缩性
【10月更文挑战第13天】Serverless架构让开发者无需管理服务器即可构建和运行应用,实现成本优化与自动扩展。本文介绍其工作原理、核心优势及实施步骤,探讨在Web应用后端、数据处理等领域的应用,并分享实战技巧。
|
14天前
|
机器学习/深度学习 人工智能 自然语言处理
Tokenformer:基于参数标记化的高效可扩展Transformer架构
本文是对发表于arXiv的论文 "TOKENFORMER: RETHINKING TRANSFORMER SCALING WITH TOKENIZED MODEL PARAMETERS" 的深入解读与扩展分析。主要探讨了一种革新性的Transformer架构设计方案,该方案通过参数标记化实现了模型的高效扩展和计算优化。
73 0
|
5天前
|
缓存 负载均衡 JavaScript
探索微服务架构下的API网关模式
【10月更文挑战第37天】在微服务架构的海洋中,API网关犹如一座灯塔,指引着服务的航向。它不仅是客户端请求的集散地,更是后端微服务的守门人。本文将深入探讨API网关的设计哲学、核心功能以及它在微服务生态中扮演的角色,同时通过实际代码示例,揭示如何实现一个高效、可靠的API网关。
|
3天前
|
Cloud Native 安全 数据安全/隐私保护
云原生架构下的微服务治理与挑战####
随着云计算技术的飞速发展,云原生架构以其高效、灵活、可扩展的特性成为现代企业IT架构的首选。本文聚焦于云原生环境下的微服务治理问题,探讨其在促进业务敏捷性的同时所面临的挑战及应对策略。通过分析微服务拆分、服务间通信、故障隔离与恢复等关键环节,本文旨在为读者提供一个关于如何在云原生环境中有效实施微服务治理的全面视角,助力企业在数字化转型的道路上稳健前行。 ####