hi,大家好,我是徐小夕,之前和大家分享了很多可視化低代碼的技術實踐,最近也做了一款非常有意思的文檔搭建引擎——Nocode/Doc:
接下來和大家分享另一個比較有意思的話題——多人協同技術。
多人協同技術方案常見的應用場景主要有:
主要目的是實現多個人同時編輯一份共享資源, 來提高工作效率。
拋開已有技術本身,我們拿最簡單的富文本編輯器為例子, 如果我們想讓它實現多人同時編輯,有哪些可以想到的方案呢?
即每個人保存時都強制以自己的版本為主,即保存最后一次修改,這樣會導致的問題是無法實現真正意義上的共享協作。
也就是對文件”上鎖“。當某個用戶正在編輯文檔時,對此文檔進行加鎖處理,避免多人同時編輯,從而避免文檔的內容沖突。 缺點就是用戶體驗不友好,并且需要等待時間。
我們可以采用類似 git 的版本管理模式,多人編輯時利用 webrtc / socket 與服務端通信,保存時通過服務端進行差異對比、合并,自動進行沖突處理,再通過服務推送如SSE(服務端實時主動向瀏覽器推送消息的技術)的方式推送給其他人。
弊端是會出現類似 git 修改同一行,純靠服務端無法處理,需要手動處理沖突。
這里給大家推薦一個有意思的庫 NodeGit。
github地址: https://github.com/nodegit/nodegit
以下是 NodeGit 的一些主要特點:
NodeGit 可以用于多個領域,例如自動化部署、協作工具、代碼分析、教育工具和 CI/CD 系統等。通過使用 NodeGit,我們能以編程方式訪問和操作 Git 存儲庫,實現更靈活和自動化的版本控制流程。
當然以上這幾種方式很難應對復雜場景的多人協作。
OT 算法是一種用于實時協同編輯的算法,它通過操作 & 轉換來實現數據的一致性。在 OT 算法中,每個用戶對數據的操作(如修改、刪除等)都被記錄下來,并在其他用戶的客戶端進行相應的轉換,從而實現多個用戶對同一份數據的協同編輯。
OT 算法的優點在于它可以實時地反映用戶的操作,并且可以很好地處理并發沖突。但是 OT 算法需要在中心化的服務器上進行協同調度,因此對于大規模的分布式系統來說不太適用。
基于 OT 的協同編輯核心是:將文檔的每一次修改看作是一個操作,即操作原子化處理,如在第 N 個位置插入一個字符時,客戶端會將操作發送到服務端去處理。
以quill富文本編輯器舉例, 它通過 retain、insert、delete 三個操作完成整篇文檔的描述與操作,如下:
[ // 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
用戶將原子化的操作發送到服務端時(必須有中央服務器進行調度), 服務端對多個客戶端的操作進行轉換,對客戶端操作中的并發沖突進行修正,確保當前操作同步到其他設備時得到一致的結果,因為對沖突的處理都是在服務端完成,所以客戶端得到的結果一定是一致的,也就是說 OT 算法的結果保證強一致性。
轉換完成后,通過網絡發送到對應用戶,用戶合并操作,從而得到一致結果。
這意味著 OT 算法對網絡要求更高,如果某個用戶出現網絡異常,導致一些操作缺失或延遲,那么服務端的轉換就會出現問題。
OT算法可視化模型:https://operational-transformation.github.io/index.html
CRDT 算法全稱 Conflict-free Replicated Data Type,即無沖突復制數據類型,是一種基于數據結構的無沖突復制數據類型算法,它通過數據結構的合并來實現數據的一致性。
在 CRDT 算法中,每個用戶對數據的修改都會被記錄下來,并在其他用戶的客戶端進行合并,以實現數據的一致性。CRDT 算法的優點在于它可以適用于大規模的分布式系統,并且不需要中心化的服務器進行協同調度。
但是,CRDT 算法在處理復雜操作時可能會存在合并沖突的問題,需要設計復雜的合并函數來解決。
Yjs 是專門為在 web 上構建協同應用程序而設計的CRDT。
CRDT 包含以下兩種:
基于狀態的 CRDT 更容易設計和實現,每個 CRDT 的整個狀態最終都必須傳輸給其他每個副本,每個副本之間通過同步全量狀態達到最終一致狀態,這可能開銷很大;
而基于操作的 CRDT 只傳輸更新操作,各副本之間通過同步操作來達到最終一致狀態,通常很小。
穿插一個小概念:
向量時鐘(Vector Clock),它是一種在分布式系統中用于記錄事件順序的時間戳機制。它的主要目的是在分布式環境中實現事件的并發控制和一致性。
向量時鐘的基本思想是為系統中的每個節點維護一個向量,其中每個分量對應一個節點,用于記錄該節點的事件發生次數。當一個節點發生事件時,它會增加自己分量的值。向量時鐘的關鍵是在不同節點之間傳遞這些向量,并在合并時確保一致性。
目前協同算法底層都會采用向量時鐘的模式來設計操作算法。
先上代碼:
const createMutex = () => { let token = true return (f, g) => { if (token) { token = false try { f() } finally { token = true } } else if (g !== undefined) { g() } }}
它用于創建一個互斥鎖(Mutex)。互斥鎖是一種用于控制資源訪問的機制,確保在任何給定的時間只有一個線程(在這里可以理解為一個函數調用)可以訪問被保護的資源或代碼塊。
下面是對代碼中每個部分的解釋:
通過這種方式,createMutex 函數創建了一個簡單的互斥鎖機制。只有在互斥鎖可用時,才能執行f函數。如果互斥鎖已被其他函數獲取,將跳過f函數的執行,并在可能的情況下執行g函數。
這種互斥鎖的實現通常用于在多線程或異步環境中確保對共享資源的安全訪問。
Yjs 本身是一個數據結構,原理是:當多人協作時,對于文檔內容修改,通過中間層將文檔數據轉換成 CRDT 數據;通過 CRDT 進行數據數據更新這種增量的同步,通過中間層將 CRDT 的數據轉換成文檔數據,另一個協作方就能看到對方內容的更新。
中間內容的更新是基于 Yjs 數據結構進行的,沖突處理等核心都是 Yjs 承擔的,通信基于 websocket 或 webrtc,所以我們只需要簡單的使用就可以實現多人協同的應用。
下面是我總結的一個結構:
Yjs 基于數據結構層面處理沖突,比 OT 更加穩健,對復雜網絡的適應性更強。網絡延時或離線編輯對數據結構來說,處理沒有任何差異。
我總結了一下它的幾個核心特點:
Yjs 提供的 Awareness(意識)模塊,名如其意,讓協作者能夠意識到其他人的位置在哪,有效避免沖突可能性。
基于 CRDT 的內容合并,天然支持離線編輯,瀏覽器端做本地化存儲。
Yjs 自身提供了快照機制,保存歷史版本不用保存全量數據,只是基于 Yjs 打一個快照,后續基于快照恢復歷史版本。
上限人數很高,可支持很多人同時編輯。
目前主流的 figma 也是采用的 CRDT 開發協同編輯功能。
以上我根據自己的理解整理了一下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事件,我們可以在這些事件中進行相應的處理。
通過將更改捆綁到單個事務中,可以減少事件調用的次數,并確保數據的一致性。在事務中,我們可以進行多種操作,如插入、刪除、修改等。
本文鏈接:http://www.tebozhan.com/showinfo-26-85549-0.html可視化+多人協同技術原理和案例分享
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com