离线请留言和离线的区别(常用问答汇总)


武汉原创辉回归,4月20日聊大模特》

本文由融云技术团队原创分享。 原标题为《IM消息数据存储结构设计》,内容已修改。

1 简介

在当下移动互联网时代,IM产品已成为不可或缺的生活部分。 微信、钉钉、QQ都是典型的社交产品,其核心功能即为IM。 一些非以IM为主功能的应用,IM能力也至关重要,例如游戏、电商直播等。

IM技术应用广泛,有必要学习和掌握相关技术。 IM庞大的技术体系中,消息系统是核心要素,其中消息的分发和存储又占据关键地位,离线消息和历史消息是其中的重中之重。

本文将结合IM消息系统的实际应用,介绍离线消息和历史消息的全面解读,以及相关的技术配合和实践。 希望由此为你的离线消息和历史消息技术设计提供最佳实践灵感。

2、相关文章:

  • 《闲鱼IM线上线下聊天数据同步机制的优化实践》
  • 《闲鱼亿级IM消息系统可靠传递的优化实践》
  • 《IM消息传递保障机制的实现(二):保证闲鱼亿级IM消息系统可靠传递的优化实践》
  • 《离线消息投递》

3.IM消息投递通用做法

通常,IM消息系统中,实时消息、离线消息、历史消息遵循如下技术思路:

在线用户:消息直接发送给目标用户,服务器不存储消息。

离线用户:服务器将消息存储在离线库中。 用户登录时主动拉取离线消息,服务器随后删除这些消息。

这种方式缺点在于是消息不持久,不支持消息漫游,降低了消息可靠性。

我们设计的消息系统中,服务器收到消息后,无论目标用户在线情况,都会转发消息并发往离线库和历史消息库。 历史消息也会同步存储在离线库中以支持消息漫游功能。

4. 什么是离线消息和历史消息?

技术上,我们定义了离线消息和历史消息:

1)离线消息:

离线消息是在用户离线期间收到的消息。大多数离线消息对用户而言较重要,具有时效性。

根据系统经验,默认保存最近7天的离线消息。

用户登录后将获取所有离线消息,根据会话展示离线消息提示(例如未读气泡)。

(从技术角度来看,用户离线原因有多种,例如网络断开、客户端崩溃、服务器发送错误等。广义上讲,无法实时发送的消息都归为“离线消息”。)

2)历史消息:

历史消息存储了用户的全部聊天记录,包括发送和接收消息。

客户端获取历史消息时通常基于会话进行分页拉取。

根据系统经验,历史消息默认存储时间为半年。具体保存周期可根据实际产品需求确定,没有硬性规定。

5. IM消息发送和存储流程

以下是系统整体的消息发送和存储流程:

当用户向服务器发送聊天消息时,消息将进入消息系统,被分发并存储。

在此过程中:对于在线用户,消息将被直接推送。但如果接收者不在线或消息推出失败,将有其他途径获取消息。接收者可以主动拉取尚未接收的消息。但无法确定接收者何处获取消息,因此需要将消息存入离线库。

消息系统离线存储过程中,为了不影响稳定性,使用MQ消息队列解耦,聊天消息实际上是异步存入离线库的(通过MQ异步IO分离,这是常见做法)。

消息分发后:消息服务将消息数据同步一份到历史消息服务,历史消息服务也将消息存储于后台。

对于新设备:需要同步消息(消息漫游能力),这也是历史消息的主要作用。客户端可以在历史消息库中拉取任一会话的所有历史消息。

6. IM离线消息和历史消息存储逻辑的区别

6.1 概述

从上图中可以看出,选择不同存储介质是因为离线消息和历史消息具有不同的业务场景和读写模式。

下面重点讨论离线消息和历史消息存储的区别。

6.2 离线消息存储方式——“扩散写入”

离线消息采用扩散写入方式存储。

每个用户都有独立的收件箱和发件箱:

以单聊为例:在两人对话中,一条消息会被写入两次,分别写入发送者的发件箱和接收者的收件箱。

在群聊场景下:写入过程将进一步扩散(分散)。如果群里有N个人,一条群消息会被写入N次。

总结:

一个用户接收到某条消息,消息将写入该用户的收件箱,而发送该条消息的用户,消息将写入其发件
箱。这就是“扩散写入”。

6.3 历史消息存储方式——“扩散读”

历史消息采用扩散读方式存储。

每个会话内,保存了该会话的全部消息。这种扩散读模式下,每个会话内的消息仅保存一次。

与扩散写方式相比,扩散读方式利弊如下:

优势:1. 读效率高,一次查询即可获取整个会话的全部内容;2. 空间占用低。

劣势:1. 写入不便,增加消息需要找到该消息所属的会话;2. 写入效率低,增加消息需要写入多个会话。

6.4 总结

在IM等应用场景中,普遍采用扩散写入的消息同步模型。一条消息生成,但可能被多次读取。这就是典型的多读少写场景。

一个优化的IM系统应在设计上平衡读写压力,避免读写压力在任一维度达到极限。

扩散写入模式也有一定弊端。例如,10000人同时写入一条消息,消息将被写入10000次。

总结:需要根据业务场景进行相应设计。以我们的IM系统为例,根据离线消息和历史消息的不同场景,采用写扩散和读扩散相结合的方式。适合的才是最好的,无需拘泥于理论。

7. IM客户端消息拉取逻辑

7.1 离线消息拉取逻辑

对于IM客户端,离线消息拉取针对全部离线消息,包括所有会话(上线时,获取离线期间的所有未获取到的离线消息)。

离线消息按照时间顺序从上往下获取。经验值为一次获取200条消息(如果离线消息过多,将分多次分页拉取,“一次”拉取可以理解为“一次”分页)。

客户端拉取离线消息的信令中应包含客户端当前缓存的最大时间戳。

从上一节图中可以看出,我们以线性结构(时间顺序)存储离线消息,服务器会根据这个时间戳向下查找离线消息。

安装或重新安
装App时,客户端的“当前客户端缓存最大时间戳”可传入0。

服务器也会缓存客户端最后一次拉取的消息时间戳,根据业务场景、客户端类型等因素决定从哪里开始拉取。如果拉取未完成,服务器会在拉取消息的回应中包含一个表示继续拉取的标志位,客户端循环拉取,直至全部拉取完成。

7.2 历史消息拉取逻辑

历史消息的获取针对单个会话。

拉取过程中需向服务器提交两个参数:

  • 会话ID
  • 最后一条历史消息的时间戳

服务器根据这两个参数定位该客户端的会话,一次获取20条