网上有很多对于IM的教程和技术博文,有亿级用户的IM架构,有各种浅谈原创自研IM架构,也有微信技术团队分享的技术文章,有些开发者想依据这些材料自研IM。现实很饱满,事实很骨感,最初做进去的产品很难达到商用规范。事实上,很多架构没有通过海量用户的考验,当然咱们也不会评判某种架构的好坏,如果开发者希图依据网上教程做出一个商用的IM,可能有点过于乐观了。本文次要从我集体角度深度分析100%开源的OpenIM架构。当然,世界上没有最完满的架构,只有最合适的架构,也没有所谓的通用计划,不同的解决方案都有其优缺点,只有最满足业务的零碎才是一个好的零碎。而且,在无限的人力、物力,综合思考工夫老本,通常须要做出很多衡量。咱们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组件。

点击学习IM技术文章

1. OpenIM官网
2.基于Tablestore Timeline的IM(即时通讯)音讯零碎架构 - 架构篇

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的整体架构

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)负责把音讯推送给在线状态的接收者;

下图是客户端发送音讯流程

音讯转发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的写入,这样既实现了冷热数据拆散,也充分利用了机器资源。

下图是音讯解决入库流程

音讯推送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无奈推送音讯,但客户端网络重连时会及时同步历史音讯,进行音讯补齐;

下图是音讯实时推送流程:

音讯同步及对齐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]的数据实现音讯对齐。

下图是音讯同步流程图

本文次要简略论述了OpenIM的架构以及音讯流程,让开发者对其有初步意识,在接下来的文章中,咱们会具体解说OpenIM服务端音讯架构,OpenIM客户端架构,同时会详细分析OpenIM如何简略高效解决音讯的可靠性、实时性、一致性和扩展性问题。