实时协作系统

实时协作系统设计——WebSocket 通信层、Operational Transformation 与 CRDT 算法选型、协作状态同步、Presence 感知、Yjs/Liveblocks 等框架实践。

实时协作系统

实时协作真正难的不是“能实时”,而是“能一致”

很多人第一次做实时协作,会把重点放在:

  • 数据是不是秒到了
  • 光标是不是在动
  • 多人是不是能一起编辑

这些都很直观。

但协作系统最难的地方,通常不是“看起来实时”。

而是:

在多人同时改、网络时延存在、连接中断和恢复反复发生的情况下,系统最终还能不能保持一致、可解释、可恢复。

这就是为什么实时协作不是“WebSocket + 一点状态同步”这么简单。

它本质上是在处理并发和一致性。

为什么协作产品会把前端工程复杂度抬高很多

普通前端应用通常默认一个核心前提:

同一时刻主要只有当前用户在控制这个界面状态。

协作系统把这个前提打破了。

你会同时面对:

  • 本地状态
  • 远端用户状态
  • 服务端协调状态
  • 延迟与乱序
  • 重连与回放

所以协作前端其实是把分布式系统的一部分复杂度拉进了浏览器。

这也是为什么做过协作系统的前端工程师,对状态和一致性的理解通常会明显更深。

先把“实时协作”定义讲清楚

更准确的定义应该是:

实时协作系统是一类允许多个参与者在共享对象上近实时地进行并发操作,并通过同步协议与冲突处理机制维持可用一致体验的系统。

这个定义里有几个关键词不能漏:

  • 多个参与者
  • 并发操作
  • 同步协议
  • 冲突处理
  • 可用一致体验

如果只说“多人同时编辑”,会低估太多工程问题。

通信层为什么不是协作系统的全部

很多团队一开始会自然地以为:

只要上 WebSocket,就有实时协作了。

这当然是第一步。

但它还离“协作系统”很远。

通信层解决的是:

  • 消息怎么尽快双向流动
  • 连接怎么维持
  • 数据怎么推送

真正更难的是:

  • 消息顺序怎么理解
  • 并发操作怎么合并
  • 掉线重连后怎么追状态
  • 不同客户端怎么重新收敛到一致结果

所以更成熟的说法应该是:

WebSocket 是协作系统的通道,不是协作系统的核心算法。

WebSocket、SSE、WebTransport 怎么看

WebSocket

到今天依然是大多数协作系统最现实的选择。

因为它足够成熟,而且双向语义非常自然。

对于:

  • 文档协作
  • presence
  • 实时状态广播

都很好用。

SSE

SSE 更适合:

  • 服务端单向推送
  • 更新流
  • 状态通知

它在严格意义上的多人协作编辑里,不如 WebSocket 自然。

但在一些轻协作场景里仍然有价值。

WebTransport

这个方向更偏未来能力。

适合更复杂的低延迟通信讨论。

但大多数前端团队今天真正会落到生产里的协作方案,主轴仍然是 WebSocket。

“同步”到底在同步什么

这是特别值得讲透的点。

很多人以为协作系统同步的是“最终文本”或“最终内容”。

这只是表层现象。

更深层地说,系统往往在同步:

  • 操作
  • 意图
  • 局部变更
  • 文档状态的可重建轨迹

这就是为什么协作系统通常不能只靠“最后一次写入覆盖”。

因为覆盖结果不一定反映多个用户的并发意图。

OT 和 CRDT 为什么会成为主轴争论

原因很简单。

它们都在回答同一个问题:

多个用户同时操作同一份共享对象时,系统怎样在并发条件下保持可接受的一致性。

OT

Operational Transformation 的核心思路,可以先粗略理解成:

并发操作进入系统后,要根据已有操作重新转换自己的位置或语义,再应用到共享状态上。

它的重点在“操作转换”。

这使它在文档编辑类场景里长期非常重要。

CRDT

CRDT 的核心思路则更像:

把数据结构本身设计成能够在分布式并发下自然收敛。

它的重点不只是“消息怎么传”,而是“结构本身就带着收敛能力”。

这也是为什么它很适合被描述成“最终一致性由数据结构保证”。

OT 和 CRDT 不是“谁更先进”的问题

这类话题最容易变成阵营争论。

更成熟的看法应该是:

它们是两类不同的一致性设计路线。

不同系统会因为:

  • 对延迟的要求
  • 数据结构形态
  • 服务端角色
  • 离线能力诉求

而做不同选择。

所以不要把这件事讲成:

“以前是 OT,现在都该用 CRDT。”

这种说法既不准确,也没什么工程价值。

Presence 为什么不是附加特效

Presence 常常看起来像:

  • 在线人数
  • 光标颜色
  • 选区位置

好像只是增强氛围感。

其实它在协作体验里有非常实在的价值。

因为 Presence 会帮助用户理解:

  • 现在还有谁在这里
  • 谁在看哪一块
  • 这个位置为什么会变化

也就是说,Presence 不只是视觉层装饰。

它是在补多人系统里的可解释性。

没有它,协作会显得“变化很多,但原因不清楚”。

协作系统最大的产品难点之一:一致性要可解释

技术上收敛到同一个状态,只是第一层。

更高一层的问题是:

用户是否能理解为什么会变成这个状态。

例如:

  • 光标为什么跳了
  • 别人的修改为什么插进这里
  • 为什么我离线改的内容回来后顺序变了

这说明协作系统不是纯算法产品。

它也在做用户对并发结果的心理模型设计。

离线与重连为什么会把难度再拉高一层

很多协作 demo 都在稳定网络下演示。

一到真实网络环境,问题会立刻复杂很多。

因为你要处理:

  • 本地操作先缓存
  • 连接恢复后再同步
  • 消息重复投递
  • 状态追赶

这时系统已经不是“实时编辑器”那么简单。

它实际上进入了更明显的分布式同步语境。

为什么 Yjs、Automerge、Liveblocks、PartyKit 代表的不是同一层东西

这是特别容易讲混的。

Yjs / Automerge

更偏协作数据结构和同步核心。

也就是说,它们更靠近“协作引擎”这层。

Liveblocks

更偏协作产品基础设施和开发体验。

它在帮助团队更快获得:

  • presence
  • room
  • hooks
  • 常见协作能力

PartyKit

更偏实时基础设施承载层。

也就是说,它更接近“你怎么搭一个实时协作的后端运行环境”。

所以成熟团队不会把这些工具混成一个“协作库排行榜”。

而是会先分清:

自己缺的是算法层、产品能力层,还是基础设施层。

中国互联网语境里为什么实时协作会有不同产品重心

中国互联网里,协作类产品当然也很多。

但很多实时需求常常还混着:

  • 社交状态
  • 直播互动
  • 多人房间
  • 教育白板
  • 企业协同

所以“实时协作”在中国语境里,往往不只指文档协作。

它更容易扩展成:

  • 实时互动
  • 在线状态同步
  • 多人操作空间

这会让很多团队更早从 presence、消息和房间模型切入,而不是直接从 OT/CRDT 文档编辑切入。

海外语境里为什么文档协作会成为默认讨论中心

海外很多协作产品语境都非常强:

  • Google Docs
  • Notion
  • Figma
  • Slack Canvas

所以一提 real-time collaboration,很多讨论会自然先落到:

  • 文档协作
  • 共享编辑
  • 白板和设计工具

这让 OT、CRDT、document model 这些词在海外讨论里存在感更强。

容易被讲浅的误区

误区一:实时协作就是 WebSocket

不对。

WebSocket 只是通道。

误区二:多人同时编辑,最后保存一下就行

这忽略了并发、乱序和一致性问题。

误区三:Presence 只是锦上添花

它其实在帮助用户理解协作系统的状态。

误区四:OT 和 CRDT 只要会背定义就够了

真正要理解的是它们在系统里各自解决了什么、牺牲了什么。

面试或技术分享里怎么讲更成熟

一个更成熟的说法可以是:

实时协作系统真正难的不是把消息尽快发出去,而是在多人并发、网络延迟和断线重连下,仍然保持可接受的一致性和可解释的用户体验。WebSocket 只解决通信通道问题,真正的核心在于同步协议、冲突处理和 presence 设计。

OT 和 CRDT 都是在回答并发编辑时如何收敛的问题,但它们不是“新旧替代”关系,而是不同的一致性路线。团队应该根据文档模型、离线能力和基础设施约束来选择。

实践建议

实践一:先做一个最小 presence 系统

只同步:

  • 在线状态
  • 光标位置

你会很快理解“实时”本身就已经有不少边界问题。

实践二:做一个双人文本冲突实验

不要急着上完整算法。

先故意让两个人同时改同一段文本,观察最朴素实现会出现什么问题。

实践三:把协作系统分成三层画图

  • 通信层
  • 同步/冲突层
  • UI/presence 层

只要你能分清这三层,理解就会比只背术语深很多。

延展阅读