用户认为的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 提供支持。
为了便于理解,我们还是用上面的例子来说:假如收信人现在不在家,而是在酒店参加某个私人聚会,分拨中心这时只能把信交给酒店门口的安保人员,由他代为送达到收信人手中。在这里我们可以把外部接口服务理解成非邮局员工的酒店门口的安保人员。
为什么接入服务和业务处理服务要独立拆分呢?
第一点是接入服务作为消息收发的出入口,必须是一个高可用的服务,保持足够的稳定性是一个必要条件。
**
试想一下,如果连接服务总处于不稳定状态,老是出现连不上或者频繁断连的情况,一定会大大影响聊天的流畅性和用户体验。
而业务处理服务由于随着产品需求迭代,变更非常频繁,随时有新业务需要上线重启。
如果消息收发接入和业务逻辑处理都在一起,势必会让接入模块随着业务逻辑的变更上线,而频繁起停,导致已通过网络接入的客户端连接经常性地断连、重置、重连。
这种连接层的不稳定性会导致消息下推不及时、消息发送流畅性差,甚至会导致消息发送失败,从而降低用户消息收发的体验。
所以,将“只负责网络通道维持,不参与业务逻辑,不需要频繁变更的接入层”抽离出来,不管业务逻辑如何调整变化,都不需要接入层进行变更,这样能保证连接层的稳定性,从而整体上提升消息收发的用户体验。
第二点是从业务开发人员的角度看,接入服务和业务处理服务进行拆分有助于提升业务开发效率,降低业务开发门槛。
模块拆分后,接入服务负责处理一切网络通信相关的部分,比如网络的稳定性、通信协议的编解码等。这样负责业务开发的同事就可以更加专注于业务逻辑的处理,而不用关心让人头痛的网络问题,也不用关心“天书般的通信协议”了。