专栏首页黄腾霄的博客2020-6-8-关于实时协同编辑的架构思考

2020-6-8-关于实时协同编辑的架构思考

什么是协同编辑

协同编辑是指多人同时对同一份文档进行编辑。

例如我们熟悉的wiki,百度百科,以及办公产品腾讯文档,乃至我们的代码管理工具git,都可以算作是协同编辑产品。

实时协同编辑

随着大家在家办公,异地办公的情况普及,实时协同编辑工具也变得更加引人注目。

实施协同编辑会面临几个问题:

  • 实时性——输入的数据可以及时被相关协作者看到
  • 一致性——各端看到和编辑的文档需要保持一致
  • 容错性——允许存在一定的网络波动,和数据丢失

但是这三个问题会形成一个不可能三角

即任意方案只能满足其中2个点,牺牲第3个点。

有的同学可能对这个三角形不是很理解。

我们可以这样类别,将协同编辑的文档类比为分布式数据库,编辑者类别为数据库的读写服务,那么我们的这个三角形就可以转换为CAP不可能三角

关于CAP定理,可以参见我的博客2020-3-15-一文看懂CAP定理 - huangtengxiao

实施协同编辑架构抉择

架构抉择第一件要做的事情是挑出哪些点是必须要满足的,哪些点是可以妥协的。

这里我们会选择实时性和容错性:

  • 实时性:保证了用户体验,让整个产品可用,毕竟用户不会期望编辑时一直卡顿
  • 容错性:实现分布式协同和远程办公的基础,也是协同的必要条件

那为什么一致性可以妥协呢?

首先我们要基于这一个假设:

在实时协同编辑的场景下,冲突是小概率事件。

就是说大部分情况下,协同编辑的参与者都会在文档的不同部分进行操作,而很少会同时对同一区域进行操作。

因此我们需要处理一致性问题的情况较少。

另外,我们只是放弃强一致性,在各端同步之后,能够较快的恢复到完全一致的状态,实现最终一致性。

最终一致性的处理方法

diff-patch

如果熟悉Git的同学,就会发现diff-patch和git的版本管理基本一致。

当出现版本冲突时,会通过diff算法计算出,两个版本之间的差异值,然后生成一个patch,将两个版本的内容合并。

这样就能让服务器端和本地端的文档内容重新保持一致。

但是diff-patch这种方式是基于文档内容比较的,那就意味着一旦出现对同一行的操作冲突,就需要人工介入,选择其中一个版本的内容。

例如git,出现合并冲突时,需要开发者对所有冲突部分进行人工处理。否则很容易出现无法运行的代码。

这种方式适用于“编辑——保存——解决冲突”的交互方式,比如kb文档。

但是要处理googledoc或者腾讯文档这样的交互就不合适了。

Operational Transformation

Operational Transformation方法是将所有的编辑行为封装成一个个的操作。

各个编辑者执行单个操作后,会将操作信息同步到各个协作端。

协作端根据操作的执行时间戳,调整文档状态,保持各端操作顺序的一致性。

注意的一点,Operational Transformation只是一种操作思想,具体的操作实现可以按照业务情况处理。

下面是我思考的两种操作方式:

  • 操作回滚法 在每个操作执行的同时,生成对应的undo,redo方法。 如果发现需要执行更早的操作,那么就先回滚已有晚于新操作时间戳的操作。 然后再按顺序执行新操作,和刚刚回滚的操作。 这种方式对于有全局状态管理的应用来说实现会比较方便,而且服务器的工作量也比较小,只需要进行时间戳的添加,以及消息的转发。
  • 操作补偿法 操作补偿法直接在服务器端维护最终的文档状态。 每次同步时,根据各端不同的状态重新生成一个补偿操作,发给对应的协作者客户端。 客户端执行补偿操作,重新和服务端状态保持一致。 这种方式不需要客户端维持一个撤销重做栈,任务被集中到了服务端。 但是在协作者增多时服务端压力会变大。 此外获取到补偿操作之前客户端会有短暂的不可用状态(避免冲突)

交互设计

在实际的应用中,我们可以采用两者结合的方式。

  • 在线实时协作阶段,可以采用webrtc+Operational Transformation 保持一个良好的实时体验。 此时操作变更较少,冲突和补偿处理也会比较轻量,对用户感知少。
  • 在出现较长时间断线,或者离线编辑的情况下,可以在连线后进行一次diff-patch,确保较大部分的改动可以同步。

参考文档:

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 2018-10-28-WPF的只读依赖属性

    .net - How do You Create a Read-Only Dependency Property? - Stack Overflow

    黄腾霄
  • 2019-11-22-C++CLI的Ref和Out使用

    C++/CLI 是一种.NET语言,因此其可以像C#一样使用Ref和Out为函数参数进行标识。

    黄腾霄
  • 2020-3-10-PPT文档解析之母版

    分别是页面(Slide),布局(SlideLayout)和母版(SlideMaster)

    黄腾霄
  • Office文件追踪方案探索

    office套件已经成为大家日常办公必不可少的工具,丰富的文字编辑、演示文稿以及数据处理能力,无一不展示其强大的功能。然后随着形式的多样性,文件的保存和传输让o...

    FB客服
  • 深度学习与统计力学(III) :神经网络的误差曲面

    即使一个深层网络能够通过选择参数表达所需的函数,也不清楚什么时候可以通过(随机)梯度下降将公式(3)中的训练误差 εTrain((w,D) 下降来成功地找到这组...

    数据酷客
  • 网络可编程与验证

    作者简介:唐昊,现就职于华为,从事云网络研发工作。本文所有观点仅代表作者个人观点,与作者现在或者之前所在的公司无关。

    SDNLAB
  • 一个轻量小巧的集成在vs里的代码生成工具

    liberate是一个集成在vs里的轻量小巧的代码生成框架,当然如果您已经熟练运用CodeSmith了,您就不用考虑它了。不过也可以把它作为学习vsx...

    明年我18
  • java8第二篇

    Predicate断言式,判断是否符合指定的条件。个人感觉这个函数式接口,常用于集合的过滤操作,我们可以看下其提供的方法都有哪些。

    后端Coder
  • EasyDSS流媒体服务器和EasyDSS云平台异同

    ###相同点:### 首先:不管是EasyDSS流媒体服务器还是EasyDSS云平台,都是可以配合EasyNVR来完成摄像机的公网直播的、微信直播等功能的; ...

    EasyNVR
  • Python 4 种不同的存取文件骚操作

    前言:最近开始学习tensorflow框架,选修课让任选一种框架实现mnist手写数字的识别分类。小詹也就随着大流选择了 tf 框架,跟着教程边学边做,小詹用了...

    小小詹同学

扫码关注云+社区

领取腾讯云代金券