你好,我是陈现麟。

在上周的期中测试环节,我留了一道 IM 系统的架构设计题,相信你一定进行了深入的思考,可能还产生了一些疑问。那么在本节课中,我就来详细地解答一下,如何依据业务和架构的需求来设计一个 IM 系统。

问题回顾

首先,我们来回顾一下 IM 系统的业务和架构方面的需求。

业务上的需求

架构上的需求

问题解析

基于这些业务和架构上的需求,我完成了一个架构设计,具体见下图。接下来,我们就基于这个设计图来回答期中测试的问题。这里要特别说明一点,如果你的架构设计和我的不一样,也不一定就是错了。我们在做架构设计的时候,都是在不断地做 trade-off,很多方案没有绝对的对与错,只有深入理解业务,才能做出更适合业务场景的架构设计。

1.IM 系统一般都会涉及基于 TCP 的长连接通道和基于 HTTP(S) 的短连接通道,你认为长连接通道和短连接通道的职责分别是什么?

长连接在客户端和服务器端都需要维护状态,并且消息是异步收发的,我们对长连接的设计应该尽量简单,而短连接可以理解为无状态的,并且请求是同步处理的,方便去完成一些复杂的功能,所以我认为一个比较好的职责划分方式是:

2.长连接的就近接入和负载均衡应该怎么来做?(可以考虑通过设计一个路由服务来解决。)

设计一个路由服务,客户端在建立长连接之前,先请求路由服务,路由服务通过客户端的 IP 或者 GPS 等位置信息,在充分考虑就近接入和负载均衡的基础上,给客户端返回最合适的接入点。

3.整个 IM 系统应该怎么分层?每一层的职责是什么?(可以考虑从长连接接入、Push 和 IM 等方面来进行分层。)

这个 IM 系统可以分为 3 层:接入层、Push 层 和 IM 层,它们具体的职责为:

4.在系统设计中,如何让功能在迭代上线的时候,不要影响到用户已经建立好的长连接呢?

在上面的分层中,我们接入层长连接服务的设计与业务无关,并且信令的数据结构易扩展,这样可以保证业务功能迭代上线时,只需要发布 IM 层的服务,而长连接服务几乎不需要迭代升级,这也就保证了在功能上线时,不会影响到用户已经建立好的长连接。

5.对于业务需求,IM 系统的消息扩散模式,采用读扩散还是写扩散?为什么?

因为业务需求为单聊和 100 人以内的群聊,所以我们可以采用写扩散的模式,为每一个用户建立一个“收件箱”,该用户在每一次收到消息后,我们都向用户的收件箱写入一条数据,这样用户在获取新消息的时候,只需要拉取收件箱的数据即可。

而对于微博这样的关注模式,一个明星用户可能有 1000 w 的粉丝,如果采用写扩散,那么一个明星用户发布一条微博,就会导致 1000 w 次写“收件箱”,所以这种情况下,我们一般采用读扩散,用户拉取微博消息列表的时候,即用户读微博信息的时候,根据关注用户发布的微博列表来生成微博消息列表

其实,很多的场景为了满足业务要求,会通过写扩散和读扩散的混合模式来进行消息的扩散,如果一条消息的接收者非常多,则采用读扩散,否则采用写扩散。

6.如何保障消息的发送接口是幂等的?

客户端在发送消息时,生成唯一 ID,唯一 ID 的生成逻辑可以按以下的方式生成:

唯一 ID = Hash(UID + DID + 时间戳 + 自增计数)

其中,UID 为用户 ID,DID 为设备 ID,自增计数为同一个时间戳下发送的消息数。然后我们可以依据第 8 讲重试幂等中的“至少一次消息传递加消息幂等性”的方式来处理。

7.如果要对 IM 系统进行限流,你认为应该在哪几个地方来实施?为什么?

我认为可以在下面三个地方进行限流:

8.如何提高长连接和短连接通道的连接成功率?

关于这个问题,你可以具体查看第 15 讲“被动故障的预案梳理”,其中关于 DNS 解析问题和网络连通性问题的预案,就能解答你的疑惑。

9.整个 IM 系统要满足业务需求的指标,大约需要多少机器资源?是怎么计算的?

这部分我们一起按照业务的需求,来估算所需要的机器数量,注意,这里只是估算,不是绝对准确的数据。

通过这一次期中测试,我们对“分布式计算篇”的知识,以及架构设计的一些思想进行了查漏补缺,如果你完成得很好,请不要骄傲,接下来的学习中还要继续温故而知新;如果你在完成时,遇到了很多问题或者成绩并不好,也不要灰心丧气,现在你知道了不擅长的部分或者不懂的知识,一定要抓紧时间认真复习,有不明白的地方欢迎和我在留言区交流,继续加油!