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

前言

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 = truereturn (f, g) => {if (token) {token = falsetry {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.mzph.cn/news/828951.shtml

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

相关文章

SpringBoot+vue开发记录(二)

说明:本篇文章的主要内容为SpringBoot开发中后端的创建 项目创建: 1. 新建项目: 如下,这样简单创建就行了,JDK什么的就先17,当然1.8也是可以的,后面可以改。 这样就创建好了: 2. pom.xml…

js修改路由参数+vue——js基础

最近在写看板,要求执行某个操作后更改路由参数,方便用户保存地址以便于下次直接获取对应的数据。 比如:原地址:http://localhost:4200/tvType/out 执行某个操作后,地址变更为:http://localhost:4200/tvTyp…

IP地址 0.0.0.0 和 127.0.0.1之间的区别

你好,我是 shengjk1,多年大厂经验,努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注!你会有如下收益: 了解大厂经验拥有和大厂相匹配的技术等 希望看什么,评论或者私信告诉我! 文章目录 一…

应用回归分析,R语音,逐步回归法,第5章

library(readr) data3_1 <- read_csv("data3.1.csv")View(data3_1) lm5<-lm(y~.,data=data3_1) lm6<-step(lm5,direction = "both") summary(lm6) 输出: Start: AIC=377.73 y ~ x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9Df Sum of Sq …

ChatGPT全方位指导:学术论文写作从零开始,轻松搞定高质量论文!

目录 文末福利 一、论文选题的深度探讨 二、撰写摘要的艺术 三、关键词的精选 四、引言的构建 五、正文的结构设计 六、撰写结论的策略 七、致谢的编写 八、附录的有效利用 九、参考文献的整理 文末有福利哦 撰写一篇高质量的学术论文是一项既复杂又耗时的任务。这个…

Android TV 桌面图标闪

<?xml version"1.0" encoding"utf-8"?> <!-- Copyright (C) 2014 The Android Open Source ProjectLicensed under the Apache License, Version 2.0 (the "License");you may not use this file except in compliance with the Lice…

MongoDB数据恢复—拷贝MongoDB数据库文件后无法启动服务的数据恢复案例

服务器数据恢复环境&#xff1a; 一台Windows Server操作系统服务器&#xff0c;服务器上部署MongoDB数据库。 MongoDB数据库故障&检测&#xff1a; 工作人员在未关闭MongoDB数据库服务的情况下&#xff0c;将数据库文件拷贝到其他分区。拷贝完成后将原MongoDB数据库所在分…

8个拿来即用的Python自动化脚本!

每天你都可能会执行许多重复的任务&#xff0c;例如阅读新闻、发邮件、查看天气、清理文件夹等等&#xff0c;使用自动化脚本&#xff0c;就无需手动一次又一次地完成这些任务&#xff0c;非常方便。而在某种程度上&#xff0c;Python 就是自动化的代名词。 今天分享 8 个非常…

医学影像增强:空间域方法与频域方法等

医学影像图像增强是一项关键技术,旨在改善图像质量,以便更好地进行疾病诊断和评估。增强方法通常分为两大类:空间域方法和频域方法。 一、 空间域方法 空间域方法涉及直接对医学影像的像素值进行操作,以提高图像的视觉质量。以下是一些常用的空间域方法: 对比度调整:通过…

html实现点击按钮时下方展开一句话

你可以使用 HTML、CSS 和 JavaScript 来实现点击按钮时展开一句话的效果。下面是一个简单的实现示例&#xff1a; <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta name"viewport" content&qu…

《逍遥游·六十八拐》

五月阳光映大观&#xff0c;艳丽队服身上穿。海埂西门集合后&#xff0c;蓝光城外相谈欢。 松茂水库映蓝天&#xff0c;阳宗镇上舞蹁跹。 六十八拐道崎岖&#xff0c;一鼓作气意志坚。 宜良宿&#xff0c;夜幕深&#xff0c;梦中山水情相牵。待破晓&#xff0c;新日升&#xf…

mongodb 分片集群认证

增加认证 副本间认证外部使用认证 如果是开启状态,先关闭路由,再关闭配置服务,最后关闭分片数据复本集中的每个mongod&#xff0c;从次节点开始。直到副本集的所 有成员都离线&#xff0c;包括任何仲裁者。主节点必须是最后一个成员关闭以避免潜在的回滚.最好通过 db.shutdow…

janus模块介绍-SIP Gateway

模块启动 默认的SIP GateWay也是https协议&#xff0c;端口为8088或者8089 如果需要在自己搭建的测试服务上测试SIP GateWay模块&#xff0c;则也需要修改为wss 具体改动如下: 找到/opt/janus/share/janus/demos/siptest.js var server "wss://" window.location…

比较好的平民衣服品牌有哪些?平价质量好短袖品牌推荐

随着气候变暖&#xff0c;夏天的持续时间似乎越来越长&#xff0c;短袖作为夏季的必备服装&#xff0c;受到了广大男士的青睐。然而&#xff0c;面对市场上众多的短袖品牌和不同的质量&#xff0c;大家都觉得选短袖的时候实在难以找到质量好且合适自己的。 选择合适的短袖确实…

C++面向对象:重写、重载、隐藏

重载、重写、隐藏的区别 重载&#xff1a;同一类中定义的同名成员函数存在重载关系&#xff0c;函数名相同&#xff0c;参数类型和数目不同&#xff0c;重载和函数是否是虚函数无关。 class A{...virtual int fun();void fun(int);void fun(double, double);static int fun(c…

第59篇:创建Nios II工程之控制LED<一>

Q&#xff1a;还记得第1篇吗&#xff1f;设计简单的逻辑电路&#xff0c;控制DE2-115开发板上LED的亮与熄灭&#xff0c;一行Verilog HDL的assign赋值语句即可实现。本期开始创建Nios II工程&#xff0c;用C语言代码控制DE2-115开发板上的LED实现流水灯效果。 A&#xff1a;在…

VPP 中注册的node是如何被调用起来的

当我们在VPP/plugins目录下注册了自己的node后&#xff0c; 肯定有一个node.func(), 那这个函数是如何执行到的呢&#xff1a; 1. 首先我们要看一下这个插件注册的时候做了什么&#xff0c; 假设node 如下&#xff1a; 编译成功后&#xff0c; 我们可以从函数vlib_plugin_earl…

回归与聚类——K-Means(六)

什么是无监督学习 一家广告平台需要根据相似的人口学特征和购买习惯将美国人口分成不同的小 组&#xff0c;以便广告客户可以通过有关联的广告接触到他们的目标客户。Airbnb 需要将自己的房屋清单分组成不同的社区&#xff0c;以便用户能更轻松地查阅这些清单。一个数据科学团队…

工作记录:vue-grid-layout 修改 margin 导致 item 高度剧烈变化

问题 用 vue-gird-layout 时发现&#xff0c;当改变 margin 值时&#xff0c;item 的尺寸也会跟着变化。 如下图&#xff1a;row height 和每个 item 的 h 都保持不变。修改 margin-y&#xff0c;item 的实际高度也跟着变了&#xff1a; 原因 研究了一番&#xff0c;发现原…

python flask 假死情况处理+https证书添加

前言 当使用flask编写了后台程序跑在服务器端的时候&#xff0c;有时候虽然后台中显示在运行&#xff0c;但是页面无法访问&#xff0c;出现这个情况可以使用如下方法修改代码&#xff0c;进而防止假死&#xff0c;另外记录下flask下证书的添加。 假死处理 出现进程存在&…