极客时间已完结课程限时免费阅读

01 | 架构与特性:一个完整的IM系统是怎样的?

01 | 架构与特性:一个完整的IM系统是怎样的?-极客时间

01 | 架构与特性:一个完整的IM系统是怎样的?

讲述:袁武林

时长14:47大小13.54M

你好,我是袁武林。在接下来的一段时间里,我将和你一起探索 IM 的相关知识。今天是第一节课,我们就先从 IM 的相关概念开始着手。
说起 IM,我估计你会先愣一下,“IM 是 QQ 或者微信这样的即时聊天系统吗?它是不是很庞大,也很复杂?”
今天我们以一个简单的 App 聊天系统为例,来看下一个简单的聊天系统都有哪些构成要素,以此来了解一个完整的 IM 系统是什么样的。

从一个简单的聊天系统说起

我们可以从使用者和开发者两个角度来看一下。

1. 使用者眼中的聊天系统

如果我们站在一个使用者的角度从直观体验上来看,一个简单的聊天系统大概由以下元素组成:用户账号、账号关系、联系人列表、消息、聊天会话。我在这里画了一个简单的示意图:
这个应该不难理解,我来解释一下。
聊天的参与需要用户,所以需要有一个用户账号,用来给用户提供唯一标识,以及头像、昵称等可供设置的选项。
账号和账号之间通过某些方式(比如加好友、互粉等)构成账号间的关系链
你的好友列表或者聊天对象的列表,我们称为联系人的列表,其中你可以选择一个联系人进行聊天互动等操作。
在聊天互动这个环节产生了消息
同时你和对方之间的聊天消息记录就组成了一个聊天会话,在会话里能看到你们之间所有的互动消息。

2. 开发者眼中的聊天系统

从一个 IM 系统开发者的角度看,聊天系统大概由这几大部分组成:客户端、接入服务、业务处理服务、存储服务和外部接口服务。
下面,我大概讲一讲每一个部分主要的职责。
首先是客户端。客户端一般是用户用于收发消息的终端设备,内置的客户端程序和服务端进行网络通信,用来承载用户的互动请求和消息接收功能。我们可以把客户端想象为邮局业务的前台,它负责把你的信收走,放到传输管道中。
其次是接入服务。接入服务可以认为是服务端的门户,为客户端提供消息收发的出入口。发送的消息先由客户端通过网络给到接入服务,然后再由接入服务递交到业务层进行处理。
接入服务主要有四块功能:连接保持、协议解析、Session 维护和消息推送。
我们可以把接入服务想象成一个信件管道,联通了邮局的前台和信件分拨中心。但是实际上,接入服务的作用很大,不仅仅只有保持连接和消息传递功能。
当服务端有消息需要推送给客户端时,也是将经过业务层处理的消息先递交给接入层,再由接入层通过网络发送到客户端。
此外,在很多基于私有通信协议的 IM 系统实现中,接入服务还提供协议的编解码工作,编解码实际主要是为了节省网络流量,系统会针对传输的内容进行紧凑的编码(比如 Protobuf),为了让业务处理时不需要关心这些业务无关的编解码工作,一般由接入层来处理。
另外,还有 session 维护的工作很多时候也由接入服务来实现,session 的作用是标识“哪个用户在哪个 TCP 连接”,用于后续的消息推送能够知道,如何找到接收人对应的连接来发送。
另外,接入服务还负责最终消息的推送执行,也就是通过网络连接把最终的消息从服务器传输送达到用户的设备上。
之后是业务处理服务。业务处理服务是真正的消息业务逻辑处理层,比如消息的存储、未读数变更、更新最近联系人等,这些内容都是业务处理的范畴。
我们可以想象得到,业务处理服务是整个 IM 系统的中枢大脑,负责各种复杂业务逻辑的处理。
就好比你的信到达分拨中心后,分拨中心可能需要给接收人发条短信告知一下,或者分拨中心发现接收人告知过要拒绝接收这个发送者的任何信件,因此会在这里直接把信件退回给发信人。
接着是存储服务。这个比较好理解,账号信息、关系链,以及消息本身,都需要进行持久化存储。
另外一般还会有一些用户消息相关的设置,也会进行服务端存储,比如:用户可以设置不接收某些人的消息。我们可以把它理解成辖区内所有人的通信地址簿,以及储存信件的仓库。
最后是外部接口服务。由于手机操作系统的限制,以及资源优化的考虑,大部分 App 在进程关闭,或者长时间后台运行时,App 和 IM 服务端的连接会被手机操作系统断开。这样当有新的消息产生时,就没法通过 IM 服务再触达用户,因而会影响用户体验。
为了让用户在 App 未打开时,或者在后台运行时,也能接收到新消息,我们会将消息给到第三方外部接口服务,来通过手机操作系统自身的公共连接服务来进行操作系统级的“消息推送”,通过这种方式下发的消息一般会在手机的“通知栏”对用户进行提醒和展示。
这种最常用的第三方系统推送服务有苹果手机自带的 APNs(Apple Push Notification service)服务、安卓手机内置的谷歌公司的 GCM(Google Cloud Messaging)服务等。
但 GCM 服务在国内无法使用,为此很多国内手机厂商在各自手机系统中,也提供类似的公共系统推送服务,如小米、华为、OPPO、vivo 等手机厂商都有相应的 SDK 提供支持。
为了便于理解,我们还是用上面的例子来说:假如收信人现在不在家,而是在酒店参加某个私人聚会,分拨中心这时只能把信交给酒店门口的安保人员,由他代为送达到收信人手中。在这里我们可以把外部接口服务理解成非邮局员工的酒店门口的安保人员。
这里,我想请你来思考一个架构问题:为什么接入服务和业务处理服务要独立拆分呢?
我们前面讲到,接入服务的主要是为客户端提供消息收发的出入口,而业务处理服务主要是处理各种聊天消息的业务逻辑,这两个服务理论上进行合并好像也没有什么不妥,但大部分 IM 系统的实现上,却基本上都会按照这种方式进行拆分。
我认为,接入服务和业务处理服务独立拆分,有以下几点原因。
第一点是接入服务作为消息收发的出入口,必须是一个高可用的服务,保持足够的稳定性是一个必要条件。
试想一下,如果连接服务总处于不稳定状态,老是出现连不上或者频繁断连的情况,一定会大大影响聊天的流畅性和用户体验。
而业务处理服务由于随着产品需求迭代,变更非常频繁,随时有新业务需要上线重启。
如果消息收发接入和业务逻辑处理都在一起,势必会让接入模块随着业务逻辑的变更上线,而频繁起停,导致已通过网络接入的客户端连接经常性地断连、重置、重连。
这种连接层的不稳定性会导致消息下推不及时、消息发送流畅性差,甚至会导致消息发送失败,从而降低用户消息收发的体验。
所以,将“只负责网络通道维持,不参与业务逻辑,不需要频繁变更的接入层”抽离出来,不管业务逻辑如何调整变化,都不需要接入层进行变更,这样能保证连接层的稳定性,从而整体上提升消息收发的用户体验。
第二点是从业务开发人员的角度看,接入服务和业务处理服务进行拆分有助于提升业务开发效率,降低业务开发门槛。
模块拆分后,接入服务负责处理一切网络通信相关的部分,比如网络的稳定性、通信协议的编解码等。这样负责业务开发的同事就可以更加专注于业务逻辑的处理,而不用关心让人头痛的网络问题,也不用关心“天书般的通信协议”了。

IM 系统都有哪些特性?

上面我们从使用者和从业者两个角度,分别了解一个完整 IM 系统的构成,接下来我们和其他系统对比着来看一下,从业务需求出发,IM 系统都有哪些不一样的特性。

1. 实时性

对于一个实时消息系统,“实时”二字很好地表达了这个系统的基本要求。
通过微信和你的好友聊天,结果等半天对方才收到,基本上也没有意愿聊了;直播场景下,如果主播的互动消息房间里的粉丝要等很长时间才能收到,也很难让粉丝们有积极参与的欲望。
了解到“实时性”在实时消息场景下的重要性后,在技术方面,我们会采用哪些手段来提升和保证这一特性呢?细节暂不展开,在第 3 篇“轮询与长连接:如何解决消息实时到达问题”中,我会和你继续探讨“保证消息实时性”的几种方案。

2. 可靠性

如果说“实时性”是即时消息被广泛应用于各种社交、互动领域的基本前置条件,那么消息的可靠性则是实时消息服务可以“被信赖”的另一个重要特性。
这里的可靠性通俗来讲,一般包括两个方面。
不丢消息。“丢消息”是互动中让人难以接受的 Bug,某些场景下可能导致业务可用性差,甚至不可用的情况。比如直播间“全员禁言”的信令消息丢失,就可能导致直播室不可控的一些情况。
消息不重复。消息重复不仅会对用户造成不必要的骚扰和困惑,可能还会导致比较严重的业务异常,比如直播间“送礼物”的消息由于某种原因被重复发出,处理不妥的话可能会导致用户损失。
那么如何做到“不丢消息”的同时,还能解决“消息重复”问题呢?对于 IM 系统可靠性的解决方案,我会在接下来的第 4 篇“ACK 机制:如何保证消息的可靠投递”,和你一起探讨。

3. 一致性

消息的一致性一般来是指:同一条消息,在多人、多终端需要保证展现顺序的一致性。
比如,对于单聊场景,一致性是指希望发送方的消息发送顺序和接收方的接收顺序保持一致;而对于一个群的某一条消息,我们希望群里其他人接收到的消息顺序都是一致的;对于同一个用户的多台终端设备,我们希望发送给这个用户的消息在多台设备上也能保持一致性。
缺少“一致性”保障的 IM 系统,经常会导致双方沟通过程中出现一些“奇妙的误会”,语言乱序相关的“惨案”。网络上,你可以想象一下发给下属、领导或合作方的几条重要工作内容,如果消息错乱了,后果可能会比较严重。
保证“消息的一致性”,也是考验即时消息系统的重要指标,那么具体在实战中都有哪些通用的技术能实现这个特性,我会后续第 5 篇“消息序号生成器:如何保证你的消息不会乱序”中详细展开。

4. 安全性

由于即时消息被广泛应用于各种私密社交和小范围圈子社交,因此用户对于系统的隐私保护能力要求也相对较高。
从系统使用安全性的角度来看,首先是要求“数据传输安全”,其次是要求“数据存储安全”,最后就是“消息内容安全”。
每一个方面实际上业界也都有比较成熟的应对方案,具体如何从这几方面入手来保障系统的整体安全性,我在第 6 篇“HttpDNS 和 TLS:你的消息聊天内容真的安全吗”中也会一一细述。
除了以上四大特性,作为一个相对高频使用的系统,消息系统在节能省电、省流量这些方面也增加了众多锦上添花的功能,在后续课程中,关于这些特点在实战方面如何落地,我也会穿插进行讲解。

小结

今天,我们先从“使用者的直观体验”和“实现上的系统构成”的两个角度,和你一起了解一个较完整的 IM 系统都应该有什么。
之后,我们又从即时消息系统所适用的业务场景需求,了解了即时消息有别于其他业务系统的四大特性。
实时性,保证消息实时触达是互动场景的必备能力。
可靠性,“不丢消息”和“消息不重复”是系统值得信赖的前置条件。
一致性,“多用户”“多终端”的一致性体验能大幅提升 IM 系统的使用体验。
安全性,“数据传输安全”“数据存储安全”“消息内容安全”三大保障方面提供全面隐私保护。
在后续课程中,我会逐步细述在主流 IM 系统的设计实现上是具体如何落地去实现“实时性”“可靠性”“一致性”“安全性”的要求。
最后,留给你一个思考题。消息一定需要在服务端的存储服务里进行存储吗?
欢迎你给我留言,我们一起讨论。
分享给需要的人,Ta购买本课程,你将得18
生成海报并分享

赞 20

提建议

上一篇
开篇词 | 搞懂“实时交互”的IM技术,将会有什么新机遇?
下一篇
02 | 消息收发架构:为你的App,加上实时通信功能
 写留言

精选留言(55)

  • 恰同学少年
    2019-08-28
    存储在服务端的作用: 1.会话一方用户不在线,上线时进行消息推送。 2.内容审查,监管,电子证据,法律要求。 3.数据分析,舆情分析。 暂时想到这些😊 是否要在服务端存储消息还是由业务与法律法规所决定的。
    展开

    作者回复: 👍

    42
  • newzai
    2019-08-29
    业务模块与接入模块的通信链路是如何设计的。接入模块收到一个消息后,通过什么方式,udp,or tcp等推送到业务模块?业务模块下发消息给用户时,怎么知道用户处于接入模块的那一个实例服务(接入模块肯定是有多个实例同时运行的)

    作者回复: 1. 接入层和业务层可以通过rpc或者mq来进行对接。 2. 推送时,可以在用户上线时维护一个全局的uid到接入网关的映射来做到定向推送,对于超大群或者直播互动场景可以不区分某一个用户落在哪个接入网关,而是让所有网关获取后来下推连到本机的用户。你想想这种方式的优势是什么呢?

    共 7 条评论
    11
  • summer
    2019-08-29
    问题1: 接入层要避免业务 如果用protobuf 怎么定义协议 避免各种业务变动影响就要把内容做抽象一些,就要用字符串 这样就失去protobuf 的效果了怎么办? 问题2 之前问过的 想知道具体一点。是要多端同步的。 收到消息就存入mysql吗?然后在从mysql取出发给用户吗?redis,pika hbase他们的角色是什么样的?

    作者回复: 问题1接入层避免业务也可以考虑使用统一协议的header,body部分直接透传二进制,或者把body的编码分委托给其他的编解码api。另外对于pb还不够紧凑的问题可以再gzip一下后再下推客户端。 问题2一般先落db,然后写离线buffer。db全量,buffer定长或者按时间过期。redis和pika一般比较适合做离线buffer,mysql和hbase一般由于消息的db存储。

    共 2 条评论
    9
  • QQ怪
    2019-08-28
    腾讯的微信好像是不存服务器的,但我觉得估计是存了的,只是没开放给我们
    9
  • 云师兄
    2019-08-28
    存储在客户端,多终端登录时候如何同步(历史)消息的?

    作者回复: 是个好问题,如果消息只存储在客户端,实现多终端消息同步基本上就会非常困难。

    6
  • 2019-11-22
    想比其他专供大厂面试的课程,这个实战的im是真的符合我的口味哈哈哈,买对课程了,im不仅仅是im,里面的技术可以营销平台,网关等出现

    作者回复: 感谢支持

    5
  • Curry
    2019-08-28
    我觉得即时消息可以放到MQ中,或者缓存中,使用数据抽取工具周期性的将数据提交服务器,进行持久化。服务器存储历史数据和近实时的数据。

    作者回复: 对,你说的这种方式其实是通过队列来异步化解耦消息存储逻辑。

    6
  • 许童童
    2019-08-28
    思考题:我觉得是需要的,如果消息接受方不在线,消息总要有一个地方存储,不是存服务器就是存发送方,存发送方显然是不行的,因为那就相当于没有发送。所以最终还是要存到服务器的,但存储形式不限于存储服务,可以是去中心化的,或者是一种缓存机制只保存几天。

    作者回复: 是的,服务端可以只是维护一个用于暂存消息和信令的离线buffer,至于存多久和产品需求以及监管需求相关。

    5
  • 一步
    2019-08-28
    接入服务层是不是就相当于网关层服务的?

    作者回复: 对,是指网关层服务。

    共 2 条评论
    5
  • 漩涡鸣人
    2019-08-29
    对im很感兴趣,给老师点个赞✺◟(∗❛ัᴗ❛ั∗)◞✺
    4
  • 东林路易斯
    2019-08-28
    存不存看需求吧。 1. 消息一定要经过服务端进行过滤验证的,用异步存储性能消耗也不大。 2. 只是存着拿来做什么,多端消息记录同步?历史记录搜索? 3. 多端同步会容易导致设备本地消息记录冲突混乱吧?历史记录搜索也不现实,数据量太大但使用率太低,有点浪费资源。 4. 就算存起来可能真的拿来自己玩,不可能对外提供服务的。例如不合法言论多重扫描鉴别,纠纷记录凭证,大数据进行关键词热点分析等等各种不可描述的商业操作。
    展开

    作者回复: 嗯,也要考虑监管方面的需求,多端同步方案合理的话不会导致冲突。

    共 2 条评论
    4
  • 影随
    2019-10-26
    老师您好,我这边有个Netty做websocket服务器的问题,望您解惑,问题如下: 用Netty创建的websocket服务,此时有Netty服务器集群,机器A,B,C 假设客户端 m,n,x 分别这样(m-A,n-B,x-C)连上了服务器,产生一个channel 。 如果我要通过服务器,通过后台管理系统,向m,n,x客户端一次性推送一次相同的消息。 问题是,我需要先找到m,n,x所对应的通道机器,找到机器后再根据channel进行推送呢?还是? 业务逻辑找机器感觉有点奇怪,望老师指点迷津。
    展开

    作者回复: 如果m n x都需要接收同样的消息,可以让接入服务器通过消息队列来订阅业务层产生的所有消息就可以啦,就不需要业务层找机器了。

    共 2 条评论
    3
  • javaworker
    2019-08-28
    老师讲的很棒啊,豁然开朗,公司以前就是做及时通讯的,前些年公司产品活跃用户很高,后来被微信打败了,哎,老同事都说公司及时通讯架构太老了,听老师一讲,各个部分都能对上号,很期待后面的课程,辛苦老师啦
    3
  • 冷笑的花猫
    2019-08-28
    请问后续老师会逐一用代码或者伪代码分解这些知识点吗?架构图会不会讲解?毕竟只是文字的话不是那么好理解,谢谢。

    作者回复: 期中左右会有一个简单版的IM的代码实现,架构图的我尽量去细化的讲,大家有问题的也可以随时给我留言提问。

    3
  • sam
    2019-08-28
    微信没有将消息存储在服务器上; Telegram将消息存储在服务器上了; 要不要进行服务端存储考虑的因素有哪些?我想产品根据自身特性和存储成本等因素来考量吧 有一个说法是(不知是否真假),腾讯不想承担大量消息存储的硬件成本,而选择不在服务端存储消息。
    展开

    作者回复: 服务端存储需要更多考虑成本和数据安全,也和产品定位有关,比如不需要支持消息多终端同步的应用,可以不在服务端进行存储。当然,还需要考虑国内监管机制是否允许的问题。

    共 9 条评论
    3
  • 0xTang
    2020-03-28
    接入服务和业务服务的通信,可以有几个,同步的有http和rpc,异步的可以使用mq
    共 1 条评论
    3
  • 行云
    2019-09-05
    数据还是要存的,用途很多,比如分析,监管调取等。有个问题,如果接入层只做接入,怎么在长链接中原路返回业务响应呢?

    作者回复: 有session维护就可以做到:知道这个用户的连接在哪台网关机上,这样处理完之后就能通过session进行response回推了。

    2
  • 楠木
    2019-08-29
    老师 你好,这个可以做基于web网页的即时通讯功能吗

    作者回复: web端也是可以的呀,思路是一样的,现在H5支持的很好了,websocket基本是标配,也是长连接的支持服务端推送的。

    共 2 条评论
    2
  • ly
    2019-08-29
    感觉IM消息的几点特性和MQ的相似,什么可靠性、顺序性、一致性,都好像。可能实现的手段应该都是类似的。 另外咨询一下老师,接入层是不是主要就是对用户的socket进行持有,包括收发请求等,最近在学网络编程、nio这一块的东西,所以对这一层特感兴趣! 一般情况下,消息是可以不储存的,例如一般的QQ用户,只会存在客户端,但是有个开通漫游消息的付费功能,这个功能应该是会储存消息的。总体还是看业务需求吧!
    展开

    作者回复: 1. 是,接入层指的是客户端连接的网关服务,一般用于连接保持、编解码、session维护等。 2. 接收方不在线时是需要服务端进行消息和信令暂存的,存多久由产品需求和监管需求决定。

    2
  • leslie
    2019-08-28
    对于老师的问题:个人觉得不一定;看实际需求。 1.一些临时会话,g统消息完全没必要保留许多天,几天就足够了 3.系统架构和服务器情况:如果只是传统的CS或BS架构没办法;可是当使用了内存库和消息队列之后,内存容易足够大的情况下完全可以只把部分消息存入服务器存储 这是个人对学习的一些见解:请老师提点。

    作者回复: 个人觉得倒是和是否是CS、BS架构关系不太大,这个主要还是看产品层面的需求吧,不考虑监管的情况下,如果产品层面不需要支持消息多终端同步,服务端可以只对接收到的消息和信令进行短时间的暂存(比如几天),有终端上线取走后就删掉;对于需要支持多终端消息同步的场景,有一端取走后消息还是需要保留一定时间。另外,用户量小、保留时间短的话这个buffer可以放内存。

    2