为什么登录成功还不够
登录完成之后,用户真正关心的通常不是“连接已经建立”,而是会话列表什么时候回来、未读数什么时候恢复、消息记录什么时候补齐、群资料和用户资料什么时候重新变得可信。也就是说,登录只是把系统带到了“已经连上”的状态,而不是直接带到了“已经可用”的状态。两者之间,还隔着一整套把远端状态重新接回本地的过程,这就是同步系统真正要处理的事情。
也正因为如此,同步不能被理解成登录后的一个小回调。对一套 IM SDK 来说,它更像是连接登录和业务服务之间的桥梁。登录负责把链路接起来,而同步负责把链路变成状态,把远端已经存在的数据和变化重新组织回本地,让消息、会话、群组、用户这些能力逐步恢复到业务可消费的样子。只有这一步真正完成,前面“已经连上”这件事,才会继续变成后面“已经能用”这件事。
先看一张总图:

这张图最关键的地方在于,同步并不直接替代消息、会话、群组这些业务能力,而是站在登录和它们之间,负责组织、推进和衔接。它首先是调度系统,然后才是数据恢复系统。
一、同步到底要接什么
如果只是普通列表业务,“同步”往往意味着重新请求一次接口,把一份结果重新拿回来。但 IM 的同步完全不是这个量级。它要接住的不是单一数据,而是一整组处于持续变化中的业务状态。主数据和消息数据不是同一种东西,会话、群组、已读时间、清空未读记录、漫游消息也不按同一个节奏变化;断网重连之后,系统不能每次都从头恢复一遍;在线通知和离线同步还可能前后交错地到达本地。
这就决定了同步系统从一开始就不能按“一次请求、一份结果”的方式去组织。它至少要同时解决几件事:不同数据域如何分别推进,哪些数据应该先恢复,哪些模块要等到前置条件成立之后再开始接管,以及某个子域如果短暂失败,能不能不拖累其他域一起回退。对 IM 来说,这些问题不是优化项,而是同步能否成立的前提。
换句话说,同步系统真正要接住的,不只是远端数据本身,而是远端状态在本地重新落稳的全过程。它处理的是恢复秩序,而不是简单搬运。
二、为什么不能只记一个时间点
理解同步系统时,最先应该抓住的,不是某个同步接口,而是时间戳模型。因为同步要不要继续、该从哪里继续、能不能只恢复局部,本质上都取决于系统如何记录每个数据域各自已经推进到了哪里。
在这套设计里,同步位置并不是由一个总时间点统一表示,而是由多组同步时间戳分别维护。消息有消息自己的进度,会话有会话自己的进度,主数据、群组数据以及某些粒度更细的成员数据,也都有各自的推进位置。这样设计看起来更复杂,但它换来的,是同步系统对真实业务变化的忠实表达。
先看时间戳模型图:

这张图要说明的重点,并不是系统里保存了多少时间戳,而是不同数据域并不共用一个总游标。这样做的好处非常直接。某个域同步失败时,不会顺手污染其他域已经推进好的位置;断线重连之后,也不需要把所有东西都从同一个时间点重新来过;当某个局部数据需要重建或重新追赶时,系统也可以只调整对应域的同步状态,而不是把全局进度一并打散。
因此,时间戳在同步系统里不是附属字段,而是整套恢复机制的地基。只有先把不同数据域的推进关系解耦开,后面所谓的增量恢复、局部重试和分域追赶才真正有可能成立。
三、为什么要一段一段同步
除了分域推进之外,同步系统还必须回答另一个问题:哪些事情应该先发生,哪些事情应该后发生。因为登录刚刚完成时,虽然系统已经和远端建立起连接,但并不意味着所有业务模块都可以同时开始工作。有些模块依赖数据库先进入可承接状态,有些模块依赖某批主数据先恢复下来,有些模块则需要等待前一个阶段结束后,才能安全地接管自己的观察和更新逻辑。
如果没有阶段划分,登录一成功,所有模块就会同时行动。表面上这像是提速,实际上很容易把系统带入顺序不清、事件互相覆盖、依赖关系难以推理的状态。对 IM 这种多域协作系统来说,这种混乱往往比同步慢一点更致命,因为它直接影响本地状态是否还能收敛成一致结果。
先看阶段推进图:

这张图最值得注意的,不是哪一个阶段叫什么,而是它体现出一种很明确的工程判断:同步不是一股脑地把所有数据推回来,而是先恢复前置主数据,再逐步唤起各个业务域进入自己的恢复过程。阶段划分的本质,也不是为了让流程更复杂,而是为了让模块启动顺序和状态依赖有地方被管理。 也正因为各个业务域是在不同阶段被逐步唤起,后面所谓“同步完成”才更像一段渐进收敛的过程,而不是所有模块在同一个时刻一起到齐。
四、同步系统到底管什么
当时间戳模型和阶段推进都成立之后,同步系统的定位就会变得非常清楚。它并不是自己替所有业务模块做完所有事情,而是负责决定什么时候开始同步、这次要同步哪些范围、应该从哪里继续、当前推进到了哪个阶段,以及在合适的时刻把结果和事件交给后面的业务服务去接住。
这意味着,同步系统做的是“推进”,而消息、会话、群组、用户这些业务服务做的是“消化”。同步系统负责把远端状态按顺序送到合适位置,业务服务则负责在自己的域内落库、更新模型、整理字段、回写进度,并继续向上提供可用状态。两者的边界如果没有分清,同步模块就会越写越重,业务模块也会越来越难独立演进。
从架构角度看,这种分工非常重要。因为一旦把同步理解成调度中心,而不是业务实现中心,就会自然明白它为什么要和登录生命周期连在一起,为什么要管理阶段和时间戳,又为什么不应该直接替会话系统或消息系统做掉它们各自的域逻辑。它的职责不是取代业务,而是让业务在正确时机接过自己的那一段恢复过程。
五、为什么同步完成不是一下子
在很多系统里,“同步完成”常常会被简化成一个布尔值,好像某个时刻一到,系统就从未完成切到已完成。但在 IM SDK 里,这种理解通常过于粗糙。因为真正的同步完成,并不是所有数据在同一瞬间一起回到本地,而是不同数据域、不同阶段、不同服务逐步进入可用状态的过程。
有些时候,主数据已经恢复了,会话列表已经能先显示出来,但消息漫游还在继续补;有些时候,主同步阶段已经结束,但某些群组扩展数据仍在后面追赶;还有些时候,系统已经足以支撑业务正常运转,但更细粒度的状态恢复仍未彻底走完。也就是说,“同步完成”更像是一条渐进收敛的曲线,而不是一个原子时刻。
这种定义虽然比单个完成标记更复杂,却更符合 IM 的真实运行方式。它允许系统在还未达到绝对完满之前,先进入“基本可用”状态;也允许不同模块依据自己的恢复程度逐步对外给出更完整的能力。对用户体验来说,这种渐进式恢复通常也比“非要等所有东西全部齐了再一次性放开”更合理。
六、这套同步为什么更适合 IM
如果进一步追问,为什么同步系统要设计得这么讲究,答案其实并不抽象。因为 IM 天生就是一个多源变化同时存在的系统。消息在变,会话在变,群组资料在变,未读状态在变,而网络条件本身也在不断变化。如果仍然试图用一个总时间点、一个总阶段、一个总完成标记去统治全部恢复过程,那么系统很快就会在真实环境里变得难以解释,也难以维持稳定。
相反,把不同数据域分别推进,把同步过程按阶段组织,把局部失败限制在局部,把在线通知和离线同步都纳入同一套秩序之中,才更符合 IM 的实际运行规律。这种设计的价值,不在于它看起来更完整,而在于它能够接受现实世界本来就不整齐这一事实。对 IM SDK 来说,稳定不是通过假设“一切都会按理想顺序到来”得到的,而是通过承认变化、分解变化并管理变化得到的。
也正因为如此,这套同步系统真正解决的,并不是“如何把一批数据拉回来”,而是“如何让一套持续变化的状态重新在本地落稳”。这才是它适合 IM 的根本原因。
七、再看同步
回到这篇文章最初的问题,鸿蒙云信 IMSDK 的同步之所以不能被理解成登录后的补充动作,是因为它承担的根本不是“顺手恢复一点数据”,而是把登录接起来之后的整段状态恢复过程组织成一套可推进、可追赶、可收敛的系统。它通过分域时间戳管理不同数据域的进度,通过阶段划分管理模块启动顺序,再通过事件和调度把这些结果逐步交给后面的业务服务去接住。
因此,理解这一篇的关键,并不是记住同步系统内部有哪些对象,而是先建立一个更重要的认识:同步模块真正负责的,是把“已经连上”推进成“逐步可用”。只有看清这一点,后面进入消息篇时,才会更容易理解消息引擎为什么不是在空中独立运转,而是在同步恢复所铺好的状态基础上继续流动。