可视化+多人协同技术原理和案例分享

news2024/11/25 2:44:19

前言

hi,大家好,我是徐小夕,之前和大家分享了很多可视化低代码的技术实践,最近也做了一款非常有意思的文档搭建引擎——Nocode/Doc

d95c0cd804877e2804b6a0f863cad2f1.png

也做了一些分享:

  • Nocode/Doc,可视化+ 零代码打造下一代文件编辑器

  • 爆肝1000小时, Dooring零代码搭建平台3.5正式上线

接下来和大家分享另一个比较有意思的话题——多人协同技术

文章大纲

  • 多人协同技术方案探讨

  • OT和CRDT算法

  • 插曲(互斥锁(Mutex)原理和代码实现)

  • yjs协同框架使用

  • yjs多人协同案例

多人协同技术方案探讨

多人协同技术方案常见的应用场景主要有:

  • 原型工具(axure,某刀,mastergo等)

  • 文档办公类 (飞书文档,钉钉文档,石墨文档等)

  • 设计工具(即时设计,figma等)

主要目的是实现多个人同时编辑一份共享资源, 来提高工作效率。

50b79a410428b7d9258fe8291d86c562.png

抛开已有技术本身,我们拿最简单的富文本编辑器为例子, 如果我们想让它实现多人同时编辑,有哪些可以想到的方案呢?

  • 覆盖模式

即每个人保存时都强制以自己的版本为主,即保存最后一次修改,这样会导致的问题是无法实现真正意义上的共享协作。

  • 锁模式

也就是对文件”上锁“。当某个用户正在编辑文档时,对此文档进行加锁处理,避免多人同时编辑,从而避免文档的内容冲突。 缺点就是用户体验不友好,并且需要等待时间。

  • diff 模式

我们可以采用类似 git 的版本管理模式,多人编辑时利用 webrtc / socket 与服务端通信,保存时通过服务端进行差异对比、合并,自动进行冲突处理,再通过服务推送如SSE(服务端实时主动向浏览器推送消息的技术)的方式推送给其他人。

弊端是会出现类似 git 修改同一行,纯靠服务端无法处理,需要手动处理冲突。

这里给大家推荐一个有意思的库 NodeGit

github地址: https://github.com/nodegit/nodegit

以下是 NodeGit 的一些主要特点:

  • 全功能:几乎支持 Git 的所有命令,如克隆、提交、拉取、合并等。

  • 高性能:直接调用 C 库,提供接近原生速度的性能。

  • 易于集成:作为一个 Node.js 模块,可轻松融入任何 Node.js 项目,无需额外的构建步骤或依赖。

  • 跨平台:支持 Windows、macOS 和 Linux,让开发者可以在各种操作系统上工作。

  • 文档齐全:提供详细的 API 文档和示例代码,便于理解和使用。

  • 社区活跃:开源社区活跃,问题和 PR 能得到及时响应,不断更新改进。

NodeGit 可以用于多个领域,例如自动化部署、协作工具、代码分析、教育工具和 CI/CD 系统等。通过使用 NodeGit,我们能以编程方式访问和操作 Git 存储库,实现更灵活和自动化的版本控制流程。

当然以上这几种方式很难应对复杂场景的多人协作。

OT和CRDT算法

OT 算法是一种用于实时协同编辑的算法,它通过操作 & 转换来实现数据的一致性。在 OT 算法中,每个用户对数据的操作(如修改、删除等)都被记录下来,并在其他用户的客户端进行相应的转换,从而实现多个用户对同一份数据的协同编辑。

OT 算法的优点在于它可以实时地反映用户的操作,并且可以很好地处理并发冲突。但是 OT 算法需要在中心化的服务器上进行协同调度,因此对于大规模的分布式系统来说不太适用。

操作 Operational

基于 OT 的协同编辑核心是:将文档的每一次修改看作是一个操作,即操作原子化处理,如在第 N 个位置插入一个字符时,客户端会将操作发送到服务端去处理。

quill富文本编辑器举例, 它通过 retaininsertdelete 三个操作完成整篇文档的描述与操作,如下:

[
    // Unbold and italicize "Gandalf"
    { retain: 7, attributes: { bold: null, italic: true } },

    // Keep " the " as is
    { retain: 5 },

    // Insert "White" formatted with color #fff
    { insert: 'White', attributes: { color: '#fff' } },

    // Delete "Grey"
    { delete: 4 }
  ]

相关地址:https://quilljs.com/docs/delta

Transformation 转换
d150a5d22bfd826dd33506b3f70bde8e.png

用户将原子化的操作发送到服务端时(必须有中央服务器进行调度), 服务端对多个客户端的操作进行转换,对客户端操作中的并发冲突进行修正,确保当前操作同步到其他设备时得到一致的结果,因为对冲突的处理都是在服务端完成,所以客户端得到的结果一定是一致的,也就是说 OT 算法的结果保证强一致性。

转换完成后,通过网络发送到对应用户,用户合并操作,从而得到一致结果。

这意味着 OT 算法对网络要求更高,如果某个用户出现网络异常,导致一些操作缺失或延迟,那么服务端的转换就会出现问题。

OT算法可视化模型:https://operational-transformation.github.io/index.html

CRDT

CRDT 算法全称 Conflict-free Replicated Data Type,即无冲突复制数据类型,是一种基于数据结构的无冲突复制数据类型算法,它通过数据结构的合并来实现数据的一致性

CRDT 算法中,每个用户对数据的修改都会被记录下来,并在其他用户的客户端进行合并,以实现数据的一致性。CRDT 算法的优点在于它可以适用于大规模的分布式系统,并且不需要中心化的服务器进行协同调度。

但是,CRDT 算法在处理复杂操作时可能会存在合并冲突的问题,需要设计复杂的合并函数来解决。

Yjs 是专门为在 web 上构建协同应用程序而设计的CRDT.

CRDT 包含以下两种:

  • CmRDT:基于操作的 CRDT,OP-based-CRDT

  • CvRDT:基于状态的 CRDT,State-based CRDT

基于状态的 CRDT 更容易设计和实现,每个 CRDT 的整个状态最终都必须传输给其他每个副本,每个副本之间通过同步全量状态达到最终一致状态,这可能开销很大;

而基于操作的 CRDT 只传输更新操作,各副本之间通过同步操作来达到最终一致状态,通常很小。

穿插一个小概念:

向量时钟(Vector Clock),它是一种在分布式系统中用于记录事件顺序的时间戳机制。它的主要目的是在分布式环境中实现事件的并发控制和一致性。

向量时钟的基本思想是为系统中的每个节点维护一个向量,其中每个分量对应一个节点,用于记录该节点的事件发生次数。当一个节点发生事件时,它会增加自己分量的值。向量时钟的关键是在不同节点之间传递这些向量,并在合并时确保一致性。

目前协同算法底层都会采用向量时钟的模式来设计操作算法。

插曲(互斥锁(Mutex)原理和代码实现)

先上代码:

const createMutex = () => {
    let token = true
    return (f, g) => {
        if (token) {
            token = false
            try {
                f()
            } finally {
                token = true
            }
        } else if (g !== undefined) {
            g()
        }
    }
}

它用于创建一个互斥锁(Mutex)。互斥锁是一种用于控制资源访问的机制,确保在任何给定的时间只有一个线程(在这里可以理解为一个函数调用)可以访问被保护的资源或代码块

下面是对代码中每个部分的解释:

  • let token = true:创建一个名为token的变量,并将其初始化为true。token用于表示互斥锁的状态。

  • return (f, g) => { ... }:返回一个箭头函数,该函数接受两个参数f和g。

  • if (token) { ... }:如果token为true,表示互斥锁可用。

  • token = false:将token设置为false,表示当前函数获取了互斥锁。

  • try { f() } finally { token = true }:在try块中执行传入的函数f。如果在执行f的过程中发生异常,会跳转到finally块中。在finally块中,将token重新设置为true,表示释放互斥锁。

  • else if (g !== undefined) { g() }:如果token为false,表示互斥锁已被其他函数获取。如果同时还传递了第二个参数g,则执行g函数。

通过这种方式,createMutex 函数创建了一个简单的互斥锁机制。只有在互斥锁可用时,才能执行f函数。如果互斥锁已被其他函数获取,将跳过f函数的执行,并在可能的情况下执行g函数。

这种互斥锁的实现通常用于在多线程或异步环境中确保对共享资源的安全访问

yjs协同框架使用

Yjs 本身是一个数据结构,原理是:当多人协作时,对于文档内容修改,通过中间层将文档数据转换成 CRDT 数据;通过 CRDT 进行数据数据更新这种增量的同步,通过中间层将 CRDT 的数据转换成文档数据,另一个协作方就能看到对方内容的更新。

中间内容的更新是基于 Yjs 数据结构进行的,冲突处理等核心都是 Yjs 承担的,通信基于 websocketwebrtc,所以我们只需要简单的使用就可以实现多人协同的应用。

下面是我总结的一个结构:

13f77f4337e38ccf95806a20723273e5.png

Yjs 基于数据结构层面处理冲突,比 OT 更加稳健,对复杂网络的适应性更强。网络延时或离线编辑对数据结构来说,处理没有任何差异。

我总结了一下它的几个核心特点:

  • 协同列表及光标位置

Yjs 提供的 Awareness(意识)模块,名如其意,让协作者能够意识到其他人的位置在哪,有效避免冲突可能性。

  • 离线编辑

基于 CRDT 的内容合并,天然支持离线编辑,浏览器端做本地化存储。

  • 版本历史支持

Yjs 自身提供了快照机制,保存历史版本不用保存全量数据,只是基于 Yjs 打一个快照,后续基于快照恢复历史版本。

  • 系统编辑人数上限

上限人数很高,可支持很多人同时编辑。

目前主流的 figma 也是采用的 CRDT 开发协同编辑功能。

yjs使用
dc9f4086640f4f6693619b64aa2524f5.png

以上我根据自己的理解整理了一下yjs的核心模块。接下来我以数组结构为例子给大家介绍一下它的用法:

import * as Y from 'yjs'

const ydoc = new Y.Doc()

// 1: 定义一个类型为数组的共享数据结构
const yarray = ydoc.getArray('my doc') 


// 2. 向数组中插入数据,在第一个位置插入3条数据
yarray.insert(0, [1, 2, 3]) 
// 3. 在第二个位置删除一条数据
yarray.delete(1, 1)
// 4. 获取可用的结果
yarray.toArray() // => [1, 3]

// 5. 监听数据变化,执行操作
yarray.observeDeep((event) => {
  console.log(event)
})

// 将连续的操作合并到transact 中
ydoc.transact(() => {
  yarray.insert(1, ['a', 'b'])
  yarray.delete(2, 2) // deletes 'b' and 2
}) // => [{ retain: 1 }, { insert: ['a'] }, { delete: 1 }]

transact方法用于执行事务操作。事务是共享文档上的一系列更改,这些更改会在一个事务中进行处理,以保证数据的一致性和正确性。每个事务都会触发Observer调用和update事件,我们可以在这些事件中进行相应的处理。

通过将更改捆绑到单个事务中,可以减少事件调用的次数,并确保数据的一致性。在事务中,我们可以进行多种操作,如插入、删除、修改等。

yjs多人协同案例

feef638f871da3315ee58fb521c2ad59.png

最后

好啦。这就是本周的更新,预计4月29号会做一波更大规模的更新和功能上线,欢迎随时和我留言反馈,建议,技术交流~

大家也可以关注我的视频号,后续会做更多的零代码技术产品分享~

往期精彩:
  • Nocode/Doc,可视化+ 零代码打造下一代文件编辑器

  • 爆肝1000小时, Dooring零代码搭建平台3.5正式上线

  • 可视化表单&试卷搭建平台技术详解

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1626711.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Flink学习(八)-Flink 集群搭建

1,准备三台机器 装好 jdk 等必要的组件。 注意,由于本身没有额外的 dns做转发。因此,需要在每台机器的 host 文件里,配置好相关 ip 具体方法 vim /etc/hosts 2,flink做配置 2.1 资源规划 节点服务器 node01 nod…

VScode Failed to parse remote port from server output

在使用VScode 在连接AutoDL 过程中一直连接不上,显示 Failed to parse remote port from server output 在网上查了很多资料,貌似的没啥用。和我有相同 error 的可以尝试修改setting.json 文件。 添加这条命令(我的json文件里面没有&#…

Spring Cloud OpenFeign 开启 httpclient5

环境介绍 java&#xff1a;17 SpringBoot&#xff1a;3.2.0 SpringCloud&#xff1a;2023.0.0 knife4j &#xff1a; 4.4.0 开始工作 pom文件引入配置 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-start…

探索设计模式的魅力:AI赋能分层模式,解构未来,智领风潮

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 探索设计模式的魅力&#xff1a;AI赋能分层模式&#xff0c;解构未来&#xff0c;智领风潮 ✨欢迎…

【优秀AI项目】每日跟踪 OpenVoice ,AI快站,OpenVoice

持续更新好玩的开源AI项目或AI商业应用体验 一起来玩转AI&#xff01;&#xff01; 1 huggingface 国内镜像站&#xff1a;AI 快站 HUggingface被墙了&#xff0c;emmmmm 所以我之前玩模型的一大感觉就是 下载什么模型之类的太难受了&#xff01;服了 看到一个镜像站——…

leetcode1143. 最长公共子序列(ACM模式解法)

题目描述 给你一个序列X和另一个序列Z&#xff0c;当Z中的所有元素都在X中存在&#xff0c;并且在X中的下标顺序是严格递增的&#xff0c;那么就把Z叫做X的子序列。 例如&#xff1a;Z是序列X的一个子序列&#xff0c;Z中的元素在X中的下标序列为<1,2,4,6>。 现给你两个…

HarmonyOS-Next开源三方库 MPChart:打造出色的图表体验

点击下载源码https://download.csdn.net/download/liuhaikang/89228765 简介 随着移动应用的不断发展&#xff0c;数据可视化成为提高用户体验和数据交流的重要手段之一。在 OpenAtom OpenHarmony&#xff08;简称“OpenHarmony”&#xff09;应用开发中&#xff0c;一个强大而…

边缘计算在视频监控领域的应用

一、边缘计算在视频监控领域的应用 运用边缘计算解决视频监控问题&#xff0c;可以带来许多优势。以下是一些具体的应用示例&#xff1a; 实时分析与处理&#xff1a;在视频监控系统中&#xff0c;边缘计算盒子可以实时处理和分析视频流&#xff0c;实现对监控画面的智能识别…

2024电子、电路与控制工程国际学术会议(IACECIE2024)

2024电子、电路与控制工程国际学术会议&#xff08;IACECIE2024) 会议简介 2024年国际电子、电路与控制工程学术会议&#xff08;IACCECIE2024&#xff09;将于2024年在重庆隆重举行。本次会议汇集了来自全球电子、电路和控制工程领域的杰出学者、研究人员和行业代表&#…

底层逻辑(1) 是非对错

底层逻辑(1) 是非对错 关于本书 这本书的副标题叫做&#xff1a;看清这个世界的底牌。让我想起电影《教父》中的一句名言&#xff1a;花半秒钟就看透事物本质的人&#xff0c;和花一辈子都看不清事物本质的人&#xff0c;注定是截然不同的命运。 如果你看过梅多丝的《系统之美…

Power BI数据刷新 - 网关 数据源凭据详解

众所周知&#xff0c;如果在Power BI云服务中设置数据源自动刷新&#xff0c;有两种方式供你选择, 分别是&#xff1a; Gateway and cloud connections&#xff08;网关和云连接&#xff09;&#xff1b;Data Source Credentials&#xff08;数据源凭据&#xff09;&#xff1…

Linux——NFS网络文件系统

在生产环境中共享宿主目录可以用于集中管理账户 一、存储设备 DAS 是直连存储相当于移动硬盘 NAS 是网络文件系统&#xff0c;挂载后可以直接访问 SAN 存储区域网络 IPSAN 网线连接 共享的是设备&#xff0c;需要挂载后分区使用 FCSAN 光纤连接 二、服务的管理 1、安…

如何在PostgreSQL中创建一个新的数据库,并指定所有者?

文章目录 解决方案示例代码 PostgreSQL是一个强大的开源关系型数据库管理系统&#xff0c;它允许用户创建和管理多个数据库。在PostgreSQL中创建一个新的数据库并指定所有者是一个常见的操作。下面&#xff0c;我们将详细解释如何执行这一操作&#xff0c;并提供示例代码。 解…

OpenHarmony鸿蒙南向开发案例:【智能加湿器】

样例简介 智能加湿器具有实时监控其所处环境温度、湿度&#xff0c;并通过数字管家设置日程&#xff0c;自动打开加湿器控制湿度功能。显示界面使用DevEco Studio 编写的js应用&#xff0c;具有很好的兼容和移植特性。硬件上采用了带有HDF框架的驱动模型&#xff0c;通过GPIO和…

信号带宽和上升沿时间

我们在抽取高速信号的S参数时避不开的一个环节是设置仿真带宽,经常听到有人讲要设置基频(奈奎斯特频率)的4倍or 5倍带宽,如果是这样,就有一个问题:如果是56Gbps的NRZ信号,那仿真带宽真要设置到100G以上吗?这么宽的带宽,计算时间和所耗费的资源可想而知!第二个常见的问…

【Win】PsPing:深入网络性能测试与故障排查

在维护 Azure 虚拟机的过程中&#xff0c;可能会遇到一些网络连通性的问题。例如&#xff0c;当您尝试从个人 PC 上 ping 虚拟机的公网 IP 地址时&#xff0c;可能会发现出现 “Request time out” 的信息&#xff0c;导致无法 ping 通。这种情况的发生&#xff0c;通常是因为在…

python基础之元组、集合和函数的定义与返回值

1.元祖 1.元祖的定义 元组的数据结构跟列表相似 特征&#xff1a;有序、 有序&#xff1a;有&#xff08;索引/下标/index&#xff09; 正序、反序标识符&#xff1a; ( ) 里面的元素是用英文格式的逗号分割开来关键字&#xff1a;tuple 列表和元组有什么区别&#xff1f; 元组…

Golang操作Redis

一. Redis介绍 1.1 简介 Redis是完全开源免费的&#xff0c;遵循BSD协议&#xff0c;是一个高性能的key-value数据库。 Redis与其它的key-value缓存产品有以下三个特点&#xff1a; Redis支持数据持久化&#xff0c;可以见内存中的数据报错在磁盘中&#xff0c;重启的时候可以…

OpenHarmony实战开发-按钮 (Button)

Button是按钮组件&#xff0c;通常用于响应用户的点击操作&#xff0c;其类型包括胶囊按钮、圆形按钮、普通按钮。Button做为容器使用时可以通过添加子组件实现包含文字、图片等元素的按钮。具体用法请参考Button。 创建按钮 Button通过调用接口来创建&#xff0c;接口调用有…

APISix如何配置gzip压缩、cache、跨域

网上查到的apisix的配置很多都很古老&#xff0c;要改配置文件。其实现在apisix都是使用插件方式实现各种配置&#xff0c;很方便。这里简单介绍下三个常用插件、gzip压缩、cache缓存和跨域插件。这里均使用apisix的Dashboard看板进行配置。 gzip压缩 1. 打开apisix看板&#…