IM

IM(Instant Message),即时消息的简称,聊天、直播、在线客服、物联网等所有需要实时互动、高实时性的场景,还有生活中接触到的联机游戏、视频会议、在线协作等场景

架构

IM 系统的架构主要包括客户端、接入层、逻辑层、存储层四个方面。

  • 客户端:与服务端进行网络通信,收发消息。
  • 接入层:也是网关层,为客户端收发消息提供出入口。
  • 逻辑层:负责各项功能的核心逻辑的实现。
  • 存储层:负责数据的持久化存储,包括账号信息、消息内容等。

image.png

架构细分

一个支持用户点对点聊天的消息收发架构主要包括三部分:消息存储、消息未读和消息收发通道。

image.png

3.1 消息存储

场景1假设收发双方的历史消息都是相互独立的,即一方发送消息后删除了消息,另一方仍可获取到这条消息

则消息的存储需要用到两张表:

  1. 消息内容表(图中的 user_message 表)

主要存储消息ID、消息内容、消息类型、消息创建时间等

  1. 消息索引表(图中的 user_message_history 表)。

可以理解为历史聊天记录,记录了收发双方的用户ID,通过消息ID和前者关联。

场景2一般 IM 系统还需要一个最近联系人, 使互动双方能快速查找需要聊天的对象,联系人列表还会携带两人最近一条聊天消息用于展示
列表(图中的 user_message_contacter 表),该表与消息索引表的区别在于:消息索引表存储收发双方的历史消息记录,联系人表主要用于查询某个用户最近的所有联系人。

假设张三给李四发送了一条消息,会先向消息内容表插入一条数据(假设是图中 user_message 表 ID 为 1001 的记录),然后向消息索引表插入两条数据,一条是用户ID为张三的记录(假设是图中 user_message_history 表 ID 为 30923 的记录),一条是用户ID为李四的记录(假设是图中 user_message_history 表 ID 为 30922 的记录),两条记录的消息ID都是 1001。
同时会分别更新张三的最近联系人和李四的最近联系人,前者是查找联系人表中是否有用户ID为张三USERID且互动人ID为李四USERID的记录,如果没有,则插入一条新的联系人记录,最新消息ID就是 1001;反之,如果张三和李四之前已经有过聊天记录,就更新最新消息ID即可。同样的办法更新李四的最近联系人。

如果想列出张三与李四的对话消息记录,可以使用如下 SQL 语句查询:
select * from user_message_history where user_id = 张三USERID or contacter_id = 张三USERID;

3.2 消息未读

如果一方发送消息,而接收方不在线或限制通知栏提醒权限,则需要有未读提醒来作为补救措施。具体实现是设置一个未读消息总数和针对某个接收方会话的消息未读数。
当张三给李四发送消息,IM 服务端接收到消息后,给李四的总未读数加 1,给李四和张三的会话未读数加 1;
李四查看这条消息后会执行未读数变更,将李四的总未读数减 1,将李四和张三的会话未读数减 1。

一般,需要支持“消息多终端漫游”的功能,未读数存储在 IM 服务端,反之选择本地存储即可。

3.3 消息收发通道

  • 发送通道

客户端 和 IM 服务端之间维持一个 TCP 长连接,IM 服务端提供发送消息的 API;
当客户端有消息发送时,会以私有协议封装这条消息,然后调用 API 把消息发给 IM 服务端。

  • 接收通道

IM 服务端的网关服务和接收消息的客户端之间维持一个长连接(TCP长连接 或 Websocket长连接),借助 TCP 能同时接收与发送数据的能力,把消息从 IM 服务端推送给接收方。若接收方不在线(无网络或未打开APP),可借助第三方操作系统系别的辅助通道、各种设备的厂商通道,将消息通过通知栏的方式推送给接收方。