一、什么是IM系统

其实所谓的IM就是指聊天软件啦,比如微信,QQ等。
但想要实现一个IM的服务端架构其实并不容易,要考虑的东西有很多。

二、网络协议

在想设计之前,其实要先搞清楚自己想要用什么协议,或什么平台来做。比如是使用 http 协议,还是使用 tcp 协议就是一个值得思考的问题。那我们分开来讨论。

1.http 协议

我觉的 http 的核心好处就是可以使用完善的 java 生态,比如 spring 等。这个好处对于一些小公司来说,简单是非常棒的,因为不需要考虑那么多,而且 java 还非常好招人,基本代码结构搭建起来后,应界生也可以来写逻辑代码了,所以使用 http 的成本要小很多。而且使用 http 可以支持网页端。

但难免 http 也有自己的问题,首先就是要引入 websocket 这个是不可避免的, 不然不能让客户端次次都主动来接取消息。

另外一个就是接口暴露的问题,http 的接口基本是明着的,需要考虑如果设计防护。

还有一个核心问题就是安全性,http 可以被代理服务器拦截,有极大可能会使聊天内容泄露,想要制作端到端的加密比较困难。

2.tcp 协议

tcp 协议的好处就是可以自定二进制协议,比如使用 json 传递数据,而重点是,使用 tcp 即可以自己确定数据的方式,也意味着可以做一些自制的加密方案,做到端到端加密。

另一个好处就是可以保持长连接,从而为状态增加对应数据,可以做到连接与用户信息绑定,当连接断开,即用户下线。

但缺点也比较明显,需要自己实现 tcp 底层,或是使用 netty,但 netty 的文档不多,简而言之,就是生态肯定不如使用 http 协议来的爽。

三、消息协议

对于聊天软件来说,消息协议必然是重中重,如何设计一个好的消息协议呢?

通常而言,如果只是普通文本消息,我们似乎可以直接使用 utf-8 编码的字符串,但对于大多数的聊天软件而言,消息能发的不仅是文本内容,还有语音,图片,甚至是特殊的表情等等,所以使用简单的文本显示然是不够的,所以我们可以考虑使用 json 格式。

例如:

一个文本消息

{
  "type": 1,
  "data": "一个普通文本消息"
}

一张图片消息

{
  "type": 2,
  "data": {
    "url": "xxx.com/image.png,
    "size": [200, 200]
  }
}

但也那会有另一种情况,如果是可以发送图片和文本混合的消息类型呢?
那这种也可以考虑使用一个列表来。

[
  {
    "type": 1,
    "data": "一个普通文本消息"
  },
  {
    "type": 2,
    "data": {
      "url": "xxx.com/image.png,
      "size": [200, 200]
    }
  }
]

当然,这里只是做了个举例,具体数据显然不会这么简单。

四、聊天消息漫游

当然,除了微信,大部分聊天消息都是要支持漫游的,但这也是一大痛点,如果用户量很大,如何保存好全部的消息是一个问题。

首先,如果用户量较大,聊天消息较多,如何应对这么大的负载?而且还有一个更头疼的问题,我们要保存这些聊天记录多久?显然这是一个另人头疼的问题。

通常来说,用户需要查七天之前的消息的需求就已经很少了,所以一般要做数据切换,即冷数据和热数据。

比如说我们要保存用户的聊天消息一年,那七天之后的消息数据就可以投入冷数据库了。

冷数据库的负载较低,通常由热数据库定时写入,只有特殊时候才需要查询,而且客户端是有本地消息缓存的,也就是说在这种情况下需要去查冷数据库的请求很低,基本用于数据保存。

而热数据库分布式则是必然的了,或是直接使用如 tidb 等适合的分布式数据库。

五、服务器设计

如果使用 tcp 协议的话,用户在线状态与其 socket 连接有关,如连接在则在线,连接断开则离线。这种情况下,多节点服难免遇到,我想查一个用户是否在线,只能挨个节点去轮询。

所以在这种情况下通常会设计一个特殊的节点服用于记录用户在线状态。

如果使用 http 协议,则是要单独设计在线模式,比如用心跳等。

另外对于用户缓存的保存也要设计独立的缓存服,无论 http 还是 tcp 最好都要有对应的缓存,可以使用 redis 保存。