默认
打赏 发表评论 8
想开发IM:买成品怕坑?租第3方怕贵?找开源自已撸?尽量别走弯路了... 找站长给点建议
融云技术分享:全面揭秘亿级IM消息的可靠投递机制
阅读(150455) | 评论(8 收藏2 淘帖2 1
微信扫一扫关注!

本文由融云技术团队原创分享,原题“IM 消息同步机制全面解析”,为使文章更好理解,即时通讯网对内容进行了重新归纳和细节修订。


1、内容概述


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

综合考虑业务场景、系统复杂度、网络流量、终端能耗等,我们的亿级分布式IM消息系统精心设计了消息收发机制,并不断打磨优化,形成了现在的消息可靠投递机制。

整体思路就是:

  • 1)客户端、服务端共同配合,互相补充;
  • 2)采用多重机制,从不同层面保障;
  • 3)拆分上下行,分别处理。

本文根据融云亿级IM消息系统的技术实践,总结了分布式IM消息的可靠投递机制,希望能为你的IM开发和知识学习起到抛砖引玉的作用。

融云技术分享:全面揭秘亿级IM消息的可靠投递机制_cover-opti.png

本文已同步发布于“即时通讯技术圈”公众号,欢迎关注。公众号上的链接是:点此进入

2、推荐阅读


以下是相关文章汇总,有兴趣可以一并阅读:


以下是融云技术团队分享的其它文章:


3、客户端与服务端消息交互整体原理


3.1概述


一个完整的IM消息交互逻辑,通常会为两段:

  • 1)消息上行段:即由消息发送者通过IM实时通道发送给服务端;
  • 2)消息下行段:由服务端按照一定的策略送达给最终的消息接收人。

3.2消息上行段


消息上行段主要就是依赖IM的实时通道将消息传递给服务端。

这个阶段的消息可靠投递,需要从协议层进行保证,协议层需要提供可靠、有序的双向字节流传输,我们是通过自研的通信协议 RMTP(即 RongCloud Message Transfer Protocol)实现的。

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

RMTP协议交互示意图:
融云技术分享:全面揭秘亿级IM消息的可靠投递机制_1.png

如上图所示,协议层通过 QoS、 ACK 等机制,保证IM消息上行段数据传输的可靠性。

3.3消息下行段


经过总结,消息下行段主要有三种行为。

1)客户端主动拉取消息,主动拉取有两个触发方式:

  • ① 拉取离线消息:与 IM 服务新建立连接成功,用于获取不在线的这段时间未收到的消息;
  • ② 定时拉取消息:在客户端最后收到消息后启动定时器,比如 3-5 分钟执行一次。主要有两个目的,一个是用于防止因网络、中间设备等不确定因素引起的通知送达失败,服务端客户端状态不一致,一个是可通过本次请求,对业务层做状态机保活。

2)服务端主动-发送消息(直发消息):

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

3)服务端主动-发送通知(通知拉取):

这是在线消息发送机制之一,简单理解为服务端给客户端发送一个通知,通知包含时间戳等可作为排序索引的内容,客户端收到通知后,依据自身数据,对比通知内时间戳,发起拉取消息的流程。

这种场景适用于较多消息传递:比如某人有很多大规模的群,每个群内都有很多成员正在激烈讨论。通过通知拉取机制,可以有效的减少客户端服务端网络交互次数,并且对多条消息进行打包,提升有效数据载荷。既能保证时效,又能保证性能。

客户端服务端下行段消息交互示意图:
融云技术分享:全面揭秘亿级IM消息的可靠投递机制_4.png

4、客户端与服务端消息交互具体实现


正如上节所言,我们将消息的交互流程进行了拆分:即拆分出上下行。

4.1上行


在上行过程保证发送消息顺序,为了保证消息有序, 最好的方式是按照 userId 区分,然后使用时间戳排序。那么分布式部署情况下,将用户归属到固定的业务服务器上(PS:指的是同一账号的不同端固定连接到相同的业务服务器上),会使得上行排序变得更容易。同时归属到同一个服务器,在多端维护时也更容易。

客户端连接过程:

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

示意图如下:
融云技术分享:全面揭秘亿级IM消息的可靠投递机制_5.png

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

以上业务流程我们称为上行流程,上行过程存储的消息为发件箱消息。

PS:关于消息ID,需要补充说明一下:

我们采用全局唯一的消息 ID 生成策略。保证消息可通过 ID 进行识别,排重。消息ID的结构如下图所示。

融云技术分享:全面揭秘亿级IM消息的可靠投递机制_3?.png

如何实现分布式场景下唯一 ID 生成,具体请阅读:《IM消息ID技术专题(三):解密融云IM产品的聊天消息ID生成策略》。

4.2下行


消息节点在处理完上行流程后,消息按照目标用户投递到所在消息节点,进入下行流程。

下行过程,按照目标 userId 以及本消息在上行过程中生成的时间戳,计算是否需要更新时间戳(正向)。

如果需要更新则对时间戳进行加法操作,直到当前用户时间戳不重复。

如此处理后,目标用户的存储以及客户端接收到消息后的排重可以做到一致,并且可以做到同一个会话内的时间戳是有序的。从而保证同一个接收用户的消息不会出现乱序。

至此:我们已经介绍完了消息的下行交互过程,消息下行过程中的具体实现方式并不简单,以下将详细展开。

1)直发消息:

即服务端主动发送(给目标客户端)的消息:

  • 1)客户端 SDK 依据本地存储的最新消息时间戳判断,用来做排序等逻辑;
  • 2)对同一个用户直发消息1条,其他转通知。通知拉取时候客户端选择本地最新一条消息时间戳作为开始拉取时间;
  • 3)在消息发送过程中,如果上一条消息发送流程未结束,下一条消息则不用直发(s_msg),而是用通知(s_ntf)。

直发逻辑示意图:
融云技术分享:全面揭秘亿级IM消息的可靠投递机制_6.png

2)通知拉取:

即服务端主动发送通知(给目标客户端):

  • 1)服务端在通知体中携带当前消息时间戳。投递给客户端;
  • 2)客户端收到通知后,比对本地消息时间戳,选择是否发拉取消息信令;
  • 3)服务端收到拉取消息信令后,以信令携带的时间戳为开始,查询出消息列表(200 条或者 5M),并给客户端应答;
  • 4)客户端收到后,给服务端 ack,服务端维护状态;
  • 5)客户端拉取消息时使用的时间戳,是客户端本地最新一条消息的时间戳。

示意图:
融云技术分享:全面揭秘亿级IM消息的可靠投递机制_7.png

在上图中,3-7 步可能需要循环多次,有以下考虑:

  • a、客户端一次收到的消息过多,应答体积过于庞大,传输过程对网络质量要求更高, 因此按照数量以及消息体积分批次进行;
  • b、一次拉取到的消息过多,客户端处理会占用大量资源,可能会有卡顿等,体验较差。

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

主要涉及到的是状态机的更新。

下面示意图集成直发消息与通知拉取过程针对状态机的更新:
融云技术分享:全面揭秘亿级IM消息的可靠投递机制_8.png

至此,消息收发的整个核心流程介绍完毕,余下的内容将介绍多端在线的消息同步处理。

5、多端在线的消息同步


多端按照消息的上下行两个阶段,同样区分为发送方多端同步以及接收方多端同步。

5.1发送方多端同步


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

具体逻辑是:

  • 1)用户多个终端链接成功后,发送一条消息,这个消息到达 CMP(IM 接入服务) 后,CMP 做基础检查,然后获此用户的其他终端连接;
  • 2)服务把客户端上行的消息,封装为服务端下行消息,直接投递给用户的其他客户端。这样完成了发送方的多端抄送,然后将这条消息投递到 IM 服务。进入正常发送投递流程。

针对上面的第2)点,发送方的多端同步没有经过 IM Server,这么做的好处是:

  • 1)比较快速;
  • 2)经过越少的服务节点,出问题的几率越小。

5.2接收方多端同步


具体逻辑是:

  • 1)IM 服务收到消息后,先判断接收方的投递范围,这个范围指的是接收方用户的哪些的终端要接收消息;
  • 2)IM 服务将范围以及当前消息,发送到 CMP,CMP 依据范围,匹配接收方的终端,然后投递消息。

接收方多端消息同步范围的应用场景,一般都是针对所有终端。

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

融云技术分享:全面揭秘亿级IM消息的可靠投递机制_9.png

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

附录:更多IM架构设计的文章


浅谈IM系统的架构设计
简述移动端IM开发的那些坑:架构设计、通信协议和客户端
一套海量在线用户的移动端IM架构设计实践分享(含详细图文)
一套原创分布式即时通讯(IM)系统理论架构方案
从零到卓越:京东客服即时通讯系统的技术架构演进历程
蘑菇街即时通讯/IM服务器开发之架构选择
腾讯QQ1.4亿在线用户的技术挑战和架构演进之路PPT
微信后台基于时间序的海量数据冷热分级架构设计实践
微信技术总监谈架构:微信之道——大道至简(演讲全文)
如何解读《微信技术总监谈架构:微信之道——大道至简》
快速裂变:见证微信强大后台架构从0到1的演进历程(一)
17年的实践:腾讯海量产品的技术方法论
移动端IM中大规模群消息的推送如何保证效率、实时性?
现代IM系统中聊天消息的同步和存储方案探讨
IM开发基础知识补课(二):如何设计大量图片文件的服务端存储架构?
IM开发基础知识补课(三):快速理解服务端数据库读写分离原理及实践建议
IM开发基础知识补课(四):正确理解HTTP短连接中的Cookie、Session和Token
WhatsApp技术实践分享:32人工程团队创造的技术神话
微信朋友圈千亿访问量背后的技术挑战和实践总结
王者荣耀2亿用户量的背后:产品定位、技术架构、网络方案等
IM系统的MQ消息中间件选型:Kafka还是RabbitMQ?
腾讯资深架构师干货总结:一文读懂大型分布式系统设计的方方面面
以微博类应用场景为例,总结海量社交系统的架构设计步骤
快速理解高性能HTTP服务端的负载均衡技术原理
子弹短信光鲜的背后:网易云信首席架构师分享亿级IM平台的技术实践
知乎技术分享:从单机到2000万QPS并发的Redis高性能缓存实践之路
IM开发基础知识补课(五):通俗易懂,正确理解并用好MQ消息队列
微信技术分享:微信的海量IM聊天消息序列号生成实践(算法原理篇)
微信技术分享:微信的海量IM聊天消息序列号生成实践(容灾方案篇)
新手入门:零基础理解大型分布式架构的演进历史、技术原理、最佳实践
一套高可用、易伸缩、高并发的IM群聊、单聊架构方案设计实践
阿里技术分享:深度揭秘阿里数据库技术方案的10年变迁史
阿里技术分享:阿里自研金融级数据库OceanBase的艰辛成长之路
社交软件红包技术解密(一):全面解密QQ红包技术方案——架构、技术实现等
社交软件红包技术解密(二):解密微信摇一摇红包从0到1的技术演进
社交软件红包技术解密(三):微信摇一摇红包雨背后的技术细节
社交软件红包技术解密(四):微信红包系统是如何应对高并发的
社交软件红包技术解密(五):微信红包系统是如何实现高可用性的
社交软件红包技术解密(六):微信红包系统的存储层架构演进实践
社交软件红包技术解密(七):支付宝红包的海量高并发技术实践
社交软件红包技术解密(八):全面解密微博红包技术方案
社交软件红包技术解密(九):谈谈手Q红包的功能逻辑、容灾、运维、架构等
社交软件红包技术解密(十):手Q客户端针对2020年春节红包的技术实践
社交软件红包技术解密(十一):解密微信红包随机算法(含代码实现)
即时通讯新手入门:一文读懂什么是Nginx?它能否实现IM的负载均衡?
即时通讯新手入门:快速理解RPC技术——基本概念、原理和用途
多维度对比5款主流分布式MQ消息队列,妈妈再也不担心我的技术选型了
从游击队到正规军(一):马蜂窝旅游网的IM系统架构演进之路
从游击队到正规军(二):马蜂窝旅游网的IM客户端架构演进和实践总结
从游击队到正规军(三):基于Go的马蜂窝旅游网分布式IM系统技术实践
IM开发基础知识补课(六):数据库用NoSQL还是SQL?读这篇就够了!
瓜子IM智能客服系统的数据架构设计(整理自现场演讲,有配套PPT)
阿里钉钉技术分享:企业级IM王者——钉钉在后端架构上的过人之处
微信后台基于时间序的新一代海量数据存储架构的设计实践
IM开发基础知识补课(九):想开发IM集群?先搞懂什么是RPC!
阿里技术分享:电商IM消息平台,在群聊、直播场景下的技术实践
一套亿级用户的IM架构技术干货(上篇):整体架构、服务拆分等
一套亿级用户的IM架构技术干货(下篇):可靠性、有序性、弱网优化等
从新手到专家:如何设计一套亿级消息量的分布式IM系统
企业微信的IM架构设计揭秘:消息模型、万人群、已读回执、消息撤回等
融云技术分享:全面揭秘亿级IM消息的可靠投递机制
>> 更多同类文章 ……

即时通讯网 - 即时通讯开发者社区! 来源: - 即时通讯开发者社区!

上一篇:企业微信的IM架构设计揭秘:消息模型、万人群、已读回执、消息撤回等下一篇:IM全文检索技术专题(三):网易云信Web端IM的聊天消息全文检索技术实践

本帖已收录至以下技术专辑

推荐方案
评论 8
这篇文章原稿我读了5遍才理清作者想表述的内容,所以对内容进行了重新归纳,如果我没有理解错作者的意图,重新归纳后的本文,要比原稿内容清晰,因为原稿行文逻辑确实有点乱,看的脑壳真是疼。。。
引用:JackJiang 发表于 2021-07-26 00:14
这篇文章原稿我读了5遍才理清作者想表述的内容,所以对内容进行了重新归纳,如果我没有理解错作者的意图, ...

曾对比过原文与JackJiang您重新排版之后的文章,发现很多篇优化之后阅读体验确实提升了好几个Level,想请教一下您,关于技术文章的排版有什么诀窍吗?有没有相关的一些教学课程可以推荐呢?虚心受教。
引用:椎锋陷陈 发表于 2021-08-28 09:43
曾对比过原文与JackJiang您重新排版之后的文章,发现很多篇优化之后阅读体验确实提升了好几个Level,想请 ...

没有诀窍,只是很多转载的人自已都没看过就转载出来了,是不负责任的。
只要不是我原创的文章,我都会阅读很多遍,至少要我自已理清逻辑、读懂作者的意图,然后就是重新归纳整理以及排版。如果作者是花10小时写文章,我重新归纳和排版至少也得3小时,而别人通常只想花几分钟。。。。
引用:JackJiang 发表于 2021-08-28 10:21
没有诀窍,只是很多转载的人自已都没看过就转载出来了,是不负责任的。
只要不是我原创的文章,我都会阅 ...

懂了!就是要自己先把这篇文章完整消化了,再站在自己理解的角度重新输出这篇文章。
引用:椎锋陷陈 发表于 2021-08-28 10:26
懂了!就是要自己先把这篇文章完整消化了,再站在自己理解的角度重新输出这篇文章。

对的。这跟做人一样,你自已都不愿看或没有看的文章,肯定不能扔出来敷衍别人啊,对吧
直发逻辑和通知拉取切换逻辑没太看懂,直发逻辑示意图,1.先发送直发消息 2.更新直发消息状态为false 3.客户端处理,4.客户端回复ack  5.更新直发消息状态为true ,这个图里解释 客户端给出消息确认后,更新下次依然采用直发,但是再 “服务端直发消息与通知拉取切换逻辑”里,isSending = true ,发送的是type_ntf,那这个是通知拉去啊?
所有客户端强制登陆到一台服务器 .碰到以下情况如何解决
比如客户端1,2通过hash登录到a服务器 然后a服务器挂了 于是1,2客户端登陆到b服务器
此时a服务器恢复 同时又来第三个客户端
打赏楼主 ×
使用微信打赏! 使用支付宝打赏!

返回顶部