在线协同技术调研
2023-07-19 15:19:58 1 举报
AI智能生成
在线协同技术调研
作者其他创作
大纲/内容
技术理解
Automerge(以及 Yjs 和其他 CRDT)将共享文档视为字符列表。 文档中的每个字符都有一个唯一的 ID,每当您插入到文档中时,您都会为要插入的内容命名。
Automerge (RGA) 的行为由该算法定义:<br><br>构建树,将每个Item连接到其父项<br>当一个项目有多个子项时,先按序列号再按 ID 对它们进行排序。<br>结果列表(或文本文档)通过使用深度优先遍历把树打平制作。
有严重的性能问<br>编辑轨迹有 260 000 次编辑,最终文档大小约为 100 000 个字符。<br><br>正如我上面所说,automerge 需要不到 5 分钟的时间来处理此跟踪。 这只是每秒 900 次编辑,这可能没问题。 但是当它完成时,automerge 正在使用 880 MB 的 RAM。 哇! 这是每次按键 10kb 的内存。 在高峰期,automerge 使用了 2.6 GB 的 RAM!题<br>
Yjs 不需要整篇博文来讨论如何使其更快,因为它已经非常快了,我们很快就会看到。<br>Yjs 只是将所有Item放在一个单一的列表中:<br>
yjs 算法优化,以及性能也优化好了
每一个客户端一个数据结构
富文本
协同编辑是构建在富文本编辑器之上的技术,它的实现一定程度上依赖于富文本数据模型的设计,这里介绍两个比较有代表性的数据模型:
2012 年 Quill -> Delta (github 35K)
数据结构图
定义三种操作(insert、retain、delete),编辑器产生的每一个操作记录都保存了对应的操作数据,然后用一些列的操作表达富文本内容,操作列表即最终的结果
2016 年 Slate -> JSON (github 26.8k)
数据结构
协同主要面对的问题
问题一:脏路径问题
藏路径
问题二:并发冲突问题
问题三:undos/redos 问题
问题四:工程落地问题
开源技术
1.ShareDB 方案
ShareDB is a full-stack library for realtime JSON document collaboration. It provides a Node.js server for coordinating and committing edits from multiple clients. It also provides a JavaScript client for manipulating documents, which can be run either in Node.js or in the browser.
github 5.7k
架构图
子主题
方案流程图
2.Yjs 方案
github 11.2k
提供了完善的生态
y-websocket - 提供协同编辑时的消息通讯,包含服务端实现和前端集成的SDK<br>y-protocols - 定义消息通讯协议,包括消息服务初始化、内容更新、鉴权、感知系统等<br>y-redis - 持久化数据到 Redis<br>y-indexeddb - 持久化数据到 IndexedDB
方案流程图
yjs协同编辑的逻辑
1.数据结构
子主题
总数据结构是StoreStruct Map类型,每一个用户一个id:【item,item,item】-----所有人的item一起形成一个链表
2.协同编辑的情况
1.总数据是一个链表,新增一个数据 无非是在 原有的链表上插入一个item<br>2.每一个item有唯一不变的id,所以2人同时插入,一个在头,一个在尾没有任何影响,不会冲突<br>3.有冲突的情况 只会是2人同时在一处:如MN中间插入了字符,第一人插入后变为MON, 第二人去插入发现中间已经有了冲突了<br>4.对于代码而言,同一时刻只会处理一个冲突,也就是说无论多少人同时开发,同一时刻只解决一个冲突<br>5.<b><font face="思源宋体" color="#95da69"><i style="">解决冲突:当插入一个数据 Item(left) 和 Item(right) 应该是相邻的,如果不相邻即发现冲突,------解决逻辑(先找Item(left),插入他的右边就好了, 找不到Item(left), 那就找Item(right),插入它的左边就好了)</i></font></b>
3.同步机制
4. yjs后台websocket需要做什么?
y-websocket内部也有服务的脚本 /bin/server.js 处理过程很简单,建立连接后 处理收到的消息,再发送出去
后端<br>// 导入WebSocket模块:<br>const WebSocket = require('ws');<br>console.log(WebSocket)<br><br>// 引用server类 就是说真正的服务在大对象中的Server<br>const WebSocketServer = WebSocket.Server;<br><br>// ws:localhost:8500<br>const server = new WebSocketServer({<br> port:8500<br>})<br><br>// open 事件 <br>server.on('open',()=>{ console.log("webSocket open") })<br>// 绑定close 事件<br>server.on('close',()=>{ console.log("webSocket close") })<br>// 绑定error 事件<br>server.on('error',()=>{ console.log("webSocket error")})<br><br>// 绑定connection 事件 <br>// 如果有WebSocket请求接入,server对象可以响应connection事件来处理这个WebSocket:<br>server.on('connection',(ws)=>{<br> console.log("connection 只要有人连接,那么我就会被触发,有人进来了");<br><br> // 在这里面绑定message 事件 msg是前端发送过来的数据<br> // 接收前端传递的数据,最终需要广播出去给每一个客户端<br> ws.on('message',(msg)=>{ <br> console.log("message")<br> console.log(msg)<br><br> // 那到底如何广播出去呢?server.clients 记录着保存所有连接到server上的客户端<br> // 通过forEach遍历得到每一个用户<br> server.clients.forEach(item=>{<br> // item下有个send方法,把这msg广播出去再返回给前端 前端在message事件处理函数中就会收到<br> item.send(msg)<br> })<br> })<br>})
0 条评论
下一页