这里讲的 “协同编辑”,指的是 “Collaborative Editing”,多个人同时一起编辑同一个文件,比如说 Google Docs,国内的有有道云协作、石墨文档之类的。这样的系统倒不如我们前面提到的那些应用系统那么 “火”,但是,依然具备相当的典型性。
第一印象,这样的一个系统,我们可以简单做出如下归类:
- 这是一个文件编辑系统,这是最最基础的一个功能性需求,它就好像是 Windows 下的记事本,只不过它是在线的。
- 这是一个分布式系统,客户端/浏览器可以在不同的地方,通过网络和服务端联结,用户的编辑行为转化为请求发送给服务端。
- 这是一个异步系统,编辑编辑过程中,事件都是由不同用户的浏览器/客户端上对文件的来触发的。
因此,根据上面的三条,我们可以 “没什么新意” 地归纳出一些相应的需求和限制,不过,这个系统里面还隐含了这么几个特殊的要求:
- 自动保存:所见即所得,所见即所存。因此文件的变更需要自动同步到服务端保存,并且显然一般情况下,这是一个增量同步。这个增量同步是双向的,既有客户端到服务器,也有服务器到客户端。
- 冲突处理:在多数情况下,系统要能够自动处理合并多人对于同一个文件在同一时间的修改,这个文件多数是文本文件,或者至少是内容结构化了的文件。
- 版本管理:要能够追溯修改历史,回退版本,甚至单独剔除或合并特定用户的修改等等。
- 权限管理:不同的人可以具备不同的权限,比如有的人只可以读,有的人可以写。
- 图中虚线表示控制流,也包括协同编辑文件的创建,但是实际的文件内容数据流动,是通过实线完成的。
- 上半部分表示的是一个文件创建的过程,Application Server 接受请求,通过 Metadata Service 把文件创建出来。文件赋予不同的权限组,Auth Service 管理权限记录。
- 下半部分,用户把编辑内容增量写入 Channel Service,这样的事件经过鉴权和简单处理后放入待处理队列。Collaboration Service 从队列中取出事件并处理,它在完成编辑操作以后,把 merge 完的变更事件写到另一个队列中去,并通过 Channel Service 把更新推送给另一个用户。
- 一般的系统,在服务端推送的时候建立长连接的比较多,但是这类系统在客户端推送和服务端推送两头都要建立长连接。 客户端每发生一次变更,就要把事件通知到 Channel Service,反之亦然 。之所以这样做,有两个原因,
- 一是因为数据要尽可能实时地同步;
- 二是这样的数据包一般都比较小,长连接可以减少开销。
- 所谓增量变更,常见的包括三种:insert、delete 和 retain,前两个用于对当前光标位置的字符增删操作,后者用于移动光标。
- 对于文件修改的冲突自动解决和合并,有一个这类系统使用的特定的技术,叫做 Operational Transformation(OT)。基本上就是把根据用户编辑文档的版本和字符所在位置,以及编辑行为(增加或/和删除),转换并合并编辑到实际文档中位置的一种技术。
- 对于修改历史记录的存储,肯定要存放一个基础版本加上每个变更。每个变更都很小,且按照时间序列排好,这样的变更数据使用支持索引的 KV 数据库或者列数据库都可以实现。
- 但是,如果只使用这个机制,在变更非常多的情况下,读出所有数据才能得到最新版本,效率很低,其中一个改进的办法是一定变更数量以后进行 archive,或者说对一系列变更做一个 snapshot。
- 因此,整体看来,这就是一个 Snapshot + WAL(Write Ahead Log)的典型实现。
这是《常见分布式系统设计图解》系列文章中的一篇,如果你感兴趣,请参阅汇总(目录)寻找你其它感兴趣的内容。
文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《四火的唠叨》