【融云分析】IM消息同步机制全面解析

【融云分析】IM消息同步机制全面解析

综述

即时通讯系统最基础、最重要的是消息的及时性与准确性,及时体现在送达度,准确则具体表现为不丢、不重、不乱序。

综合考虑业务场景、系统复杂度、网络流量、终端能耗等,融云精心设计了消息收发机制,并不断打磨优化,形成了现在的消息同步机制。

整体思路:

1、客户端、服务端共同配合,互相补充。

2、采用多重机制,从不同层面保障。

3、拆分上下行,分别处理。

协议层

首先,从协议层保证,协议栈需要提供可靠、有序的双向字节流传输,融云自研通信协议RMTP(RongCloud Message Transfer Protocol)。

协议交互示意图

协议层通过 qos、 ack等机制,保证数据传输的可靠性。

业务层

在关键业务,采用 ack 确认机制,配合状态机,服务感知当前业务传输状态,保障业务按照预期执行。

业务层确认机制示意图

融云即时通讯

消息ID

采用全局唯一的消息 ID 生成策略。保证消息可通过 ID 进行识别,排重。

如何实现分布式场景下唯一 ID 生成,请点击融云过往技术文章了解。

客户端服务端交互

客户端与服务端之间使用长连接,基于 RMTP 协议传输数据。

经过总结,主要用三种行为:

1、客户端主动拉取消息

主动拉取有两个触发方式:

①与 IM 服务新建立连接成功,用于获取不在线的这段时间未收到的消息。 (此处叫做获取离线消息)

②定时器触发。在客户端最后收到消息后启动定时器, 比如3-5分钟执行一次, 主要有两个目的,一个是用于防止因网络,中间设备等不确定因素引起的通知送达失败,服务端客户端状态不一致,一个是可通过本次请求,对业务层做状态机保活。

2、服务端主动-发送消息(直发消息) 在线消息发送机制之一,简单理解为服务端将消息内容直接发送给客户端,适用于消息频率较低,并且持续交互,比如二人或者群内的正常交流讨论。

3、服务端主动-发送通知(通知拉取) 在线消息发送机制之一,简单理解为服务端给客户端发送一个通知,通知包含时间戳等可作为排序索引的内容,客户端收到通知后,依据自身数据,对比通知内时间戳,发起拉取消息的流程。适用于较多消息传递。比如某人有很多大规模的群,每个群内都有很多成员正在激烈讨论。通过通知拉取机制,可以有效的减少客户端服务端网络交互次数,并且对多条消息进行打包,提升有效数据载荷。既能保证时效,又能保证性能。

客户端服务端交互示意图

业务拆分

在有了多层机制保证后,将业务进行拆分,首先将业务拆分出上下行,在上行过程保证发送消息顺序, 为了保证消息有序, 最好的方式是按照 userId 区分,然后使用时间戳排序。那么分布式部署情况下,将用户归属到固定的业务服务器上,会使得上行排序变得更容易。同时归属到同一个服务器,在多端维护时也更容易。

客户端连接过程

1。客户端通过 APP server ,获取到连接使用的 token。

2。客户端使用 token 通过导航服务,获取具体连接的 IM 接入服务器(CMP),导航服务通过 userId 计算接入服务器,然后下发,使得某一客户端可以连接在同一台接入服务器(CMP)。 示意图

上行

客户端发出消息后,通过接入服务,按照 userId 投递到指定消息服务器,生成消息 Id, 依据最后一条消息时间,确认更新当前消息的时间戳(如果存在相同时间戳则后延),然后将时间戳,以及消息 Id,通过 Ack 返回给客户端 ; 然后对上行消息使用 userId + 时间戳进行缓存以及持久化存储,后续业务操作均使用此时间戳。 (此业务流程我们成为上行流程, 上行过程存储的消息为发件箱消息)

下行

消息节点在处理完上行流程后,消息按照目标用户投递到所在消息节点, 进入下行流程。 下行过程,按照目标 userId 以及本消息在上行过程中生成的时间戳,计算是否需要更新时间戳(正向)。 如果需要更新则对时间戳进行加法操作,直到当前用户时间戳不重复。如此处理后,目标用户的存储以及客户端接收到消息后的排重可以做到一致,并且可以做到同一个回话内的时间戳是有序的。从而保证同一个接收用户的消息不会出现乱序。

至此,我们已经完成了发送过程,接收过程的消息顺序保障,那么消息流程还剩下直发与通知拉取的处理流程,以及服务端如何选择是直发还是通知拉取。

直发消息

1、客户端 SDK 依据本地存储的最新消息时间戳判断,用来做排序等逻辑。

2、对同一个用户直发消息1条,其他转通知。通知拉取时候消息按照时间戳按照客户端时间。

3、在消息发送过程中,如果上一条消息发送流程未结束,下一条消息则不实用直发(s_msg),而是用(s_ntf)

直发逻辑示意图

通知拉取

1、服务端在通知体中携带当前消息时间戳。投递给客户端。

2、客户端收到通知后,比对本地消息时间戳,选择是否发拉取消息信令。

3、服务端收到拉取消息信令后,以信令携带的时间戳为开始,查询出消息列表(200条或者5M),并给客户端应答。

4、客户端收到后,给服务端ack,服务端维护状态。

5、客户端拉取消息时使用的时间戳,使用的是客户端本地最新一条的时间戳。

示意图

上图中,3-7 步可能需要循环多次,是为了考虑如果客户端为收到的消息比较多,将消息按照数量,以及体积进行了分批次,不然一次请求可能应答过于庞大,网络质量依赖会变得更严重,另外客户端体验较差。

服务端直发消息与通知拉取切换逻辑

主要涉及到的是状态机的更新。下面示意图集成直发消息与通知拉取过程针对状态机的更新:

至此,消息收发核心流程介绍完毕,只剩下多端在线的处理。

多端在线同步

多端按照上下行,同样区分为发送方多端同步以及接收方多端同步。

一、发送方多端同步

在前面客户端连接 IM 服务过程中,我们已经将同一个用户的客户端汇聚在了同一台服务,那么维护一个 userId 的多端就会变得很简单。

1、用户多个终端链接成功后,发送一条消息,这个消息到达 CMP(IM 接入服务) 后,CMP 做基础检查,然后获此用户的其他终端连接。

2、服务把客户端上行的消息,封装为服务端下行消息,直接投递给用户的其他客户端。这样完成了发送方的多端抄送,然后将这条消息投递到 IM 服务。进入正常发送投递流程。发送方的多端同步没有经过 IM Server,这么做的好处是:1、比较快速; 2、经过越少的服务节点,出问题的几率越小。

接收方多端同步

1。IM 服务收到消息后,先判断接收方的投递范围,这个范围指的是接收方用户的哪些的终端要接收消息。

2。IM 服务将范围以及当前消息,发送到CMP,CMP 依据范围,匹配接收方的终端,然后投递消息。

区分接收方多端范围应用场景: 消息一般是所有终端。

有一些特殊业务, 比如我在A客户端上,控制另外某个端的状态,可能需要一些命令消息, 这时候需要这个作用范围,针对性的投递消息。

到此,我们分析完了有关 IM 消息核心处理流程,通过层层拆解逻辑,提供可靠的消息投递机制。

       

标签: , ,