【在线文档】多人实时协作

1,842 阅读5分钟

这是我参与11月更文挑战的第9天,活动详情查看:2021最后一次更文挑战

一、前言

协同编辑的关键是要解决数据一致性的问题

不同的协作者修改他们自己的文档副本,本地的编辑会立刻应用到本地副本,之后会传输给其他协作者,这会导致不同的副本可能会以不同的顺序应用各种修改,应该有算法来确保在协同编辑之后,所有副本最终的内容是一致的。

解决多人实时协作,三步骤:

  1. 选择合适的编辑器

  2. 利用 OT 算法设计 Text Model,处理 HTML 的转化与协作冲突

  3. 利用 Accepted Flag 控制状态,保证频繁变动的 Text Model 的正确性

冲突处理的解决方案其实已经相对成熟,包括:

  1. 编辑锁:当有人在编辑某个文档时,系统会将这个文档锁定,避免其他人同时编辑。

  2. diff-patch:基于 Git 等版本管理类似的思想,对内容进行差异对比、合并等操作,包括 GNU diff-patchMyer’s diff-patch 等方案。

  3. 最终一致性实现:包括 Operational TransformationOT)、 Conflict-free replicated data type(CRDT,称为无冲突可复制数据类型)。

对应使用:

  1. 编辑锁的实现方式简单粗暴,但会直接影响用户体验,多进程读写文件,读写锁使用。

  2. diff-patch 可以对冲突进行自助合并,也可以在冲突出现时交给用户处理。

  3. OT 算法是 Google Docs 中所采用的方案,Atom 编辑器使用的则是 CRDT

(1)OTCRDT

OTCRDT 两种方法的相似之处在于它们提供最终的一致性。

不同之处在于他们的操作方式:

  1. OT 通过更改操作来做到这一点:

    • OT 会对编辑进行操作的拆分、转换,实现冲突处理的效果
    • OT 并不包括具体的实现,因此需要项目自行实现,但可以根据项目需要进行高精度的冲突处理
  2. CRDT 通过更改状态来做到这一点:

  • 基本上,CRDT 是数据结构,当使用相同的操作集进行更新时,即使这些操作以不同的顺序应用,它们始终会收敛在相同的表示形式上

  • CRDT 有两种方法:基于操作和基于状态

OT 主要用于文本,通常常很复杂且不可扩展。

CRDT 实现很简单,但 GoogleMicrosoftCKSource 和许多其他公司依赖 OT 是有原因的,CRDT 研究的当前状态支持在两种主要类型的数据上进行协作:纯文本、任意 JSON 结构。

对于富文本编辑等更高级的结构,OT 用复杂性换来了对用户预期的实现,而 CRDT 则更加关注数据结构,随着数据结构的复杂度上升,算法的时间和空间复杂度也会呈指数上升的,会带来性能上的挑战。

因此,如今大多数实时协同编辑都基于 OT 算法来实现。


OT 算法的基本数据结构

operation 类型:

  • 插入(insert

  • 删除(delete

  • 保留(retain

插入加粗『abc』[ insert(‘abc’, { bold: true }) ]

Operation 的长度: Text Model 应用到编辑器的机理:从坐标为 0 的位置开始,依次执行 Operation

举个栗子:

  • 原文:abcde
  • 变化:[retain(2), delete(1)]
  • 结果:abde

再举个栗子:

  • 原文:Hey Edy!
  • 变化:[retain(2), insert(‘llo’), delete(1), retain(1), insert(‘world’), delete(3)]
  • 结果:Hello world!

(2)通信方式

前后端通信方式有很多种,常见的包括 HTTP 短轮询(polling)、WebsocketHTTP 长轮询(long-polling)、SSEServer-Sent Events)等。

不同的在线文档团队选用的通信方式并不一致。

例如谷歌文档上行数据使用 Ajax、下行数据使用 HTTP 长轮询推送;石墨文档上行数据使用 Ajax、下行数据使用 SSE 推送;金山文档、飞书文档、腾讯文档则都使用了 Websocket 传输。

而每种通信方式都有各自的优缺点,包括兼容性、资源消耗、实时性等,也有可能跟业务团队自身的后台架构有关系。

因此我们在设计连接层的时候,考虑接口拓展性,应该预留对各种方式的支持。



二、编辑器方案

方案:

  1. 从头造轮子:参考对象 Google DocQUIP

原理:监听键盘事件,以 canvas 或其他方式来展现内容

  1. 开源:参考对象 Etherpad

原理:实现了 text modelHTML 互通的编辑器

Text Model

HTMLText Model 转换如图:

textmodel.png

Text Model 是区别于 HTML 层级而言,对编辑器内容的另一种展现形式,如下:

例如: 加粗的文字『abc』

HTML : <b>abc</b>
Model: { data: ‘abc’,  bold: true}

支持协作条件:

  1. 编辑器的 HTML 内容与 Text Model 可以互相转化

  2. Text Model 能够处理多人的改动


Text Model 处理多人改动:

Text Model 就类似一个许多 Operation 组成的数组。

  1. Operation 来表示文档内容与改动

  2. Transform 来解决多人改动的冲突



三、问题

(1)如何版本正确处理?

客户端频繁改动,服务端频繁处理,如何保证客户端、服务端每一个改动的版本是正确的呢?

客户端的 Text Model 存储策略,同时存在三个版本的 Text Model

  • Submited Model

  • Committing Model

  • User Model

客户端 Text Model 简单流程,如图:

ot-客户端.png