我一直在尝试编写自己的 Javascript 编辑器,其功能类似于 Google Docs(允许多人同时处理)。我不明白的一件事:
假设您已经让用户 A 和用户 B 直接相互连接,网络延迟为 10 毫秒。我假设编辑器使用差异系统(正如我理解的 Docs 所做的那样),其中编辑表示为“在索引 3 处插入'文本'”,并且差异带有时间戳并强制由所有客户端按时间顺序应用。
让我们从包含以下文本的文档开始:“xyz123”
用户 A 在文档开头的时间戳 001ms 处键入“abc”,而用户 B 在时间戳 005ms 处在“xyz”和“123”之间键入“hello”。
两个用户都希望结果是:“abcxyzhello123”,但是,考虑到网络延迟:
当然,“abchello xyz123”与“abc xyz 你好123”是不一样的
除了从字面上为每个字符分配自己的唯一 ID 之外,我无法想象 Google 将如何有效地解决这个问题。
我想到的一些可能性:
我正在阅读 http://www.waveprotocol.org/whitepapers/operational-transform ,但很想听听解决此问题的所有方法。
最佳答案
根据场景的拓扑和不同的权衡,实现副本的并发更改有不同的可能性。
使用中央服务器
最常见的场景是所有客户端都必须与之通信的中央服务器。
服务器可以跟踪每个参与者的文档的外观。然后 A 和 B 都将带有更改的差异发送到服务器。然后,服务器会将更改应用到相应的跟踪文档。然后它将执行三向合并并将更改应用到主文档。然后它将主文档和跟踪文档之间的差异发送给各自的客户端。这称为 differential synchronization .
另一种方法称为操作(al)转换,类似于传统版本控制系统中的 rebase 。它不需要中央服务器,但如果您有超过 2 个参与者,拥有一个会使事情变得更容易(参见 OT FAQ)。要点是您转换一个编辑中的更改,以便该编辑假定另一个编辑的更改已经发生。例如。 A 会改变 B 的编辑 insert(3, hello)
反对其编辑insert(0, abc)
结果insert(6, hello)
.
rebase 和 OT 之间的区别在于,如果您以不同的顺序应用编辑, rebase 并不能保证一致性(例如,如果 B 将 A 的编辑与他们的编辑相反,这可能导致文档状态不同)。另一方面,OT 的 promise 是,如果您进行正确的转换,则允许任何顺序。
没有中央服务器
存在可以处理点对点场景的 OT 算法(在控制层上增加实现复杂性和增加内存使用量之间进行权衡)。 Version vector 而不是简单的时间戳可用于跟踪编辑所基于的状态。然后(取决于您的 OT 算法的能力,特别是转换属性 2),可以转换传入的编辑以适应它们接收到的顺序,或者可以使用版本向量对编辑施加部分顺序 - 在这个案例历史需要通过撤消和转换编辑来“重写”,以便它们遵守版本向量强加的顺序。
最后,还有一组基于CRDT的算法。 ,称为 WOOT、Treedoc 或 Logoot,它们试图使用允许操作通勤的特殊设计的数据类型来解决问题,因此它们的应用顺序无关紧要(这类似于您对每个字符的 ID 的想法)。这里的权衡是内存消耗和操作构建的开销。
关于algorithm - Google Docs 如何处理编辑冲突?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31092669/