<cite id="ffb66"></cite><cite id="ffb66"><track id="ffb66"></track></cite>
      <legend id="ffb66"><li id="ffb66"></li></legend>
      色婷婷久,激情色播,久久久无码专区,亚洲中文字幕av,国产成人A片,av无码免费,精品久久国产,99视频精品3
      網(wǎng)易首頁(yè) > 網(wǎng)易號(hào) > 正文 申請(qǐng)入駐

      代碼里的“和平協(xié)議”:用MoonBit實(shí)現(xiàn)無(wú)沖突的多人編輯

      0
      分享至


      作者:東燈原標(biāo)題:用 MoonBit 實(shí)現(xiàn) CRDTs 算法并構(gòu)建實(shí)時(shí)協(xié)作應(yīng)用
      一.引言

      當(dāng)你在 Google Docs 中與同事同時(shí)編輯一份文檔,或者在 Figma 中與設(shè)計(jì)師協(xié)作調(diào)整界面時(shí),你有沒(méi)有想過(guò):為什么兩個(gè)人同時(shí)在同一位置輸入文字,最終結(jié)果卻不會(huì)互相覆蓋或產(chǎn)生亂碼?

      這背后隱藏著分布式系統(tǒng)中最具挑戰(zhàn)性的問(wèn)題之一:并發(fā)沖突( Concurrent Conflict)。當(dāng)多個(gè)用戶(hù)同時(shí)編輯同一份數(shù)據(jù),且這些編輯需要在不同設(shè)備之間同步時(shí),如何保證所有人最終看到相同的結(jié)果?

      本文將帶你深入理解實(shí)時(shí)協(xié)作的核心算法演進(jìn)——從經(jīng)典的操作轉(zhuǎn)換(OT)到經(jīng)典的 CRDTs 算法 RGA,再到現(xiàn)代的 EG-Walker。我們不僅會(huì)解釋這些算法的原理,還會(huì)用 MoonBit 實(shí)現(xiàn)算法的核心邏輯,并最終展示如何用它構(gòu)建一個(gè)簡(jiǎn)單的、斷網(wǎng)重連可合并的協(xié)作編輯器。

      二.實(shí)時(shí)協(xié)作的核心挑戰(zhàn)

      假設(shè)我們要構(gòu)建一個(gè)多人協(xié)作的文本編輯器。每個(gè)用戶(hù)在自己的設(shè)備上都有一份文檔副本,當(dāng)用戶(hù)進(jìn)行編輯時(shí),操作會(huì)通過(guò)網(wǎng)絡(luò)同步給其他用戶(hù)。為了保證流暢的編輯體驗(yàn),用戶(hù)的輸入應(yīng)該立即生效,而不是等待服務(wù)器確認(rèn)。

      問(wèn)題來(lái)了:當(dāng)兩個(gè)用戶(hù)同時(shí)編輯時(shí),會(huì)發(fā)生什么?讓我們考慮這個(gè)場(chǎng)景:


      這就是并發(fā)沖突的本質(zhì):相同的操作序列,以不同順序應(yīng)用,可能產(chǎn)生不同的結(jié)果 。

      我們需要的是一種機(jī)制,無(wú)論操作以什么順序到達(dá),最終所有人看到的結(jié)果都相同,而且尊重所有編輯參與者的貢獻(xiàn)。這個(gè)性質(zhì)叫做 最終一致性 (Eventual Consistency) 。

      三.操作轉(zhuǎn)換( OT )
      1、原理與簡(jiǎn)單實(shí)現(xiàn)

      操作轉(zhuǎn)換(Operational Transformation,OT)是最早用于解決實(shí)時(shí)協(xié)作沖突的算法,誕生于 1989 年。Google Docs、Etherpad 等早期協(xié)作工具都采用了這種方案。OT 的基本思路是:既然操作之間會(huì)互相影響,那就在應(yīng)用操作之前,根據(jù)已發(fā)生的操作對(duì)其進(jìn)行“轉(zhuǎn)換”,使其適應(yīng)當(dāng)前狀態(tài)。


      看看 OT 是如何解決沖突的:

      • 初始文檔是 AB 。Alice 在位置 1 插入 X ,本地變成 AXB ;Bob 在位置 1 插入 Y ,本地變成 AYB ?,F(xiàn)在雙方需要同步對(duì)方的操作。

      • 當(dāng) Alice 收到 Bob 的操作 insert(1, 'Y') 時(shí),她不能直接執(zhí)行——因?yàn)樗呀?jīng)在位置 1 插入了 X ,后面的字符都向右移了一位。OT 發(fā)現(xiàn) Bob 的插入位置(1)不在 Alice 插入位置之前,于是把位置 +1,變成 insert(2, 'Y') 。Alice 執(zhí)行后得到 AXYB 。

      • 同樣,Bob 收到 Alice 的 insert(1, 'X') 。但 Alice 的插入位置(1)不比 Bob 的(1)大,所以不調(diào)整。Bob 直接執(zhí)行,在位置 1 插入 X ,也得到 AXYB 。

      不妨簡(jiǎn)單用 MoonBit 實(shí)現(xiàn)一下:

      enum Op {
      Insert(Int, Char) // Insert(位置, 字符)
      Delete(Int) // Delete(位置)
      }


      /// 轉(zhuǎn)換函數(shù):將 op1 轉(zhuǎn)換為在 op2 已經(jīng)執(zhí)行后的等效操作
      fn transform(op1 : Op, op2 : Op) -> Op {
      match (op1, op2) {
      (Insert(pos1, ch), Insert(pos2, _)) =>
      Insert(if pos1 <= pos2 { pos1 } else { pos1 + 1 }, ch)
      (Insert(pos1, ch), Delete(pos2)) =>
      Insert(if pos1 <= pos2 { pos1 } else { pos1 - 1 }, ch)
      // ... 其他 case 類(lèi)似
      }
      }

      2、OT 存在的問(wèn)題

      OT 在工業(yè)界有廣泛應(yīng)用(Google Docs 就基于 OT),但它有一些根本性的問(wèn)題:

      1、需要中央服務(wù)器 :OT 需要一個(gè)權(quán)威的服務(wù)器來(lái)確定操作的全局順序。沒(méi)有服務(wù)器,就無(wú)法確定誰(shuí)的操作“先”發(fā)生。

      2、轉(zhuǎn)換規(guī)則復(fù)雜度爆炸 :如果有 N 種操作類(lèi)型,就需要定義 N2 個(gè)轉(zhuǎn)換規(guī)則。當(dāng)操作類(lèi)型增多(如富文本的加粗、斜體、鏈接等),復(fù)雜度急劇上升。

      3、長(zhǎng)分支合并極慢 :如果兩個(gè)用戶(hù)離線編輯了很長(zhǎng)時(shí)間,重新連接時(shí)需要轉(zhuǎn)換大量操作,性能很差。



      四.RGA:一種經(jīng)典的序列 CRDT

      CRDT(Conflict-free Replicated Data Type)采用了完全不同的思路: 不是在收到操作后轉(zhuǎn)換它,而是設(shè)計(jì)一種數(shù)據(jù)結(jié)構(gòu),使得無(wú)論操作以什么順序應(yīng)用,結(jié)果都相同 。這就像設(shè)計(jì)一種特殊的“加法”——無(wú)論你按什么順序把數(shù)字加起來(lái),結(jié)果都一樣。數(shù)學(xué)上,這要求操作滿(mǎn)足 交換律 和 結(jié)合律 。

      1、RGA:一種經(jīng)典的序列 CRDT

      RGA(Replicated Growable Array)是 2011 年提出的一種序列 CRDT,專(zhuān)門(mén)用于解決協(xié)同文本編輯中的沖突問(wèn)題。它的核心思想很簡(jiǎn)單: 用相對(duì)位置代替絕對(duì)位置 。


      還是用 Alice 和 Bob 的例子。初始文檔是 AB ,Alice 和 Bob 同時(shí)在位置 1 插入字符——Alice 插入 X ,Bob 插入 Y 。

      OT 的做法是調(diào)整位置坐標(biāo)。但 RGA 換了個(gè)思路: 不用數(shù)字位置,用“插在誰(shuí)后面”來(lái)描述插入點(diǎn) 。

      具體來(lái)說(shuō):

      • Alice 的操作不再是“在位置 1 插入 X”,而是“在 A 后面插入 X”

      • Bob 的操作是“在 A 后面插入 Y”

      兩個(gè)操作都想插在 A 后面,怎么辦?RGA 給每個(gè)字符分配一個(gè) 全局唯一的 ID 。這個(gè) ID 由兩部分組成:

      • 用戶(hù) ID :每個(gè)用戶(hù)有一個(gè)唯一標(biāo)識(shí)(如 Alice 是 A ,Bob 是 B )

      • 本地計(jì)數(shù)器 :每個(gè)用戶(hù)維護(hù)一個(gè)遞增的計(jì)數(shù)器,每次插入新字符時(shí) +1

      所以 A@1 表示“Alice 的第 1 個(gè)操作”, B@1 表示“Bob 的第 1 個(gè)操作”。當(dāng)兩個(gè)字符想插入同一位置時(shí),比較 ID 來(lái)決定順序——先比本地計(jì)數(shù)器,計(jì)數(shù)器相同時(shí)再比用戶(hù) ID(作為 tie-breaker)。這里兩個(gè)計(jì)數(shù)器都是 1,所以比較用戶(hù) ID: B > A ,因此 B@1 排在 A@1 前面,結(jié)果就是 A → Y → X → B ,即 AYXB 。

      如果用 MoonBit 實(shí)現(xiàn),我們可以先定義每個(gè)節(jié)點(diǎn)的類(lèi)型和它的 compare 規(guī)則:

      /// 唯一標(biāo)識(shí)符
      struct ID {
      peer : UInt64 // 用戶(hù) ID
      counter : Int // 本地計(jì)數(shù)器
      } derive(Eq)


      /// 比較兩個(gè) ID,用于解決并發(fā)插入沖突
      impl Compare for ID with compare(self, other) {
      // 先比較 counter,再比較 peer(打破平局)
      if self.counter != other.counter {
      other.counter - self.counter
      } else if self.peer > other.peer { -1 }
      else if self.peer < other.peer { 1 }
      else { 0 }
      }

      插入時(shí),在目標(biāo)位置之后找到正確的插入點(diǎn)——跳過(guò)所有 ID 更大的節(jié)點(diǎn):

      /// 在 target 之后插入,返回插入位置
      fn find_insert_pos(order : Array[ID], target_pos : Int, new_id : ID) -> Int {
      let mut pos = target_pos + 1
      while pos < order.length() && new_id.compare(order[pos]) > 0 {
      pos = pos + 1 // new_id 更小,繼續(xù)往后找
      }
      pos
      }

      我們剛才假設(shè)了只有插入的情況,對(duì)于刪除問(wèn)題,RGA 采用墓碑(Tombstone)策略:刪除字符時(shí)不真正移除,只標(biāo)記為“已刪除”。

      為什么不能真刪除?考慮這個(gè)場(chǎng)景:Alice 刪除了 B,同時(shí) Bob(還沒(méi)收到刪除)在 B 后面插入 X。如果 B 真的沒(méi)了,Bob 的“在 B 后面插入”就找不到參照物了。墓碑讓 B 保留在數(shù)據(jù)結(jié)構(gòu)中,只是渲染時(shí)跳過(guò),這樣 Bob 的操作仍然有效。

      /// RGA 節(jié)點(diǎn)
      struct RGANode {
      id : ID
      char : Char
      mut deleted : Bool // 墓碑標(biāo)記
      }

      /// 刪除:標(biāo)記為墓碑
      fn RGANode::delete(self : RGANode) -> Unit {
      self.deleted = true
      }

      /// 渲染:跳過(guò)墓碑
      fn render(nodes : Array[RGANode]) -> String {
      let sb = StringBuilder::new()
      for node in nodes {
      if !node.deleted { sb.write_char(node.char) }
      }
      sb.to_string()
      }

      在上面的簡(jiǎn)單實(shí)現(xiàn)當(dāng)中,為了更加簡(jiǎn)潔易懂,我們采用的是數(shù)組來(lái)存儲(chǔ) RGA 的節(jié)點(diǎn)。而熟悉數(shù)據(jù)結(jié)構(gòu)的讀者很輕松就可以發(fā)現(xiàn):RGA 會(huì)存在頻繁的插入情況,因此鏈表也許更適配這種算法。而實(shí)際的工程中則經(jīng)常使用更加穩(wěn)健高效的結(jié)構(gòu)如 B+ Tree 或跳表實(shí)現(xiàn)它。

      2、RGA 的問(wèn)題

      RGA 解決了并發(fā)沖突問(wèn)題,不需要中央服務(wù)器,支持 P2P 同步,但它也有顯著的缺點(diǎn):

      • 元數(shù)據(jù)膨脹 :每個(gè)字符都需要存儲(chǔ) ID(工程上很容易達(dá)到 16+ 字節(jié))和前驅(qū)引用,一篇 10 萬(wàn)字的文檔,元數(shù)據(jù)可能比內(nèi)容還大。

      • 墓碑累積 :刪除的字符永遠(yuǎn)保留在內(nèi)存中。一篇編輯多次的文檔,可能 90% 的數(shù)據(jù)都是墓碑,而且文字上可能還有其他維度,比如富文本,會(huì)進(jìn)一步加劇這個(gè)缺點(diǎn)造成的影響。

      • 加載緩慢 :從磁盤(pán)加載文檔時(shí),需要重建整個(gè)數(shù)據(jù)結(jié)構(gòu),這是 O(n) 甚至 O(n log n) 的操作。



      五.Event Graph Walker:更好的方案
      1、原理介紹

      Event Graph Walker(簡(jiǎn)稱(chēng) Eg-walker)是由 Joseph Gentle 和 Martin Kleppmann 在 2024 年提出的新 CRDT 算法。

      前面我們看到,OT 操作簡(jiǎn)單(只有位置索引)但需要中央服務(wù)器;CRDT 支持 P2P 但元數(shù)據(jù)膨脹嚴(yán)重。Eg-walker 的核心洞察是:兩者可以結(jié)合,即存儲(chǔ)時(shí)用簡(jiǎn)單索引,合并時(shí)臨時(shí)構(gòu)建 CRDT。

      操作像 OT 一樣輕量,只記錄 insert(pos, char) 和 delete(pos) 。需要合并并發(fā)操作時(shí),臨時(shí)重放歷史、構(gòu)建 CRDT 狀態(tài)來(lái)解決沖突,合并完就丟掉。

      可能很多讀者會(huì)這種“臨時(shí)構(gòu)建 CRDT 解決問(wèn)題的方式”存在一些性能方面的顧慮,我們的確要承認(rèn)雖然臨時(shí)構(gòu)建確實(shí)有開(kāi)銷(xiāo),但是由于大部分時(shí)間并不需要 CRDT 參與編輯工作,只有同步并發(fā)編輯的時(shí)候才需要,而且 Eg-Walker 的性質(zhì)很明顯支持增量構(gòu)建與局部構(gòu)建,只需要從快照構(gòu)建或者再?zèng)_突區(qū)域構(gòu)建 CRDT 解決沖突即可。而且可以設(shè)想的是,在操作歷史越來(lái)越復(fù)雜的情況下,臨時(shí)構(gòu)建會(huì)比維護(hù)一個(gè)會(huì)一直增長(zhǎng)的結(jié)構(gòu)更加穩(wěn)健高效。


      2、代碼實(shí)現(xiàn) 1)基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)

      首先是操作的定義。與 RGA 使用“在某個(gè) ID 后面插入”的相對(duì)定位不同,Eg-walker 直接使用數(shù)字位置索引,就像 OT 一樣簡(jiǎn)單:

      enum SimpleOp {
      Insert(Int, String) // Insert(位置, 內(nèi)容)
      Delete(Int, Int) // Delete(位置, 長(zhǎng)度)
      }

      接下來(lái)是 事件(Event) 的定義。事件是對(duì)操作的包裝,添加了因果關(guān)系信息:

      struct Event {
      id : ID // 唯一標(biāo)識(shí)符
      deps : Array[ID] // 依賴(lài)的事件(父節(jié)點(diǎn))
      op : SimpleOp // 實(shí)際的操作內(nèi)容
      lamport : Int // Lamport 時(shí)間戳,用于排序
      }

      然后我們就可以根據(jù)他們定義出一個(gè)事件圖(Event Graph):

      struct EventGraph {
      events : Map[ID, Event] // 所有已知事件
      frontiers : Array[ID] // 當(dāng)前最新的事件 ID 集合
      }

      這里定義中的 frontiers 記錄了“當(dāng)前版本”——那些沒(méi)有被任何其他事件依賴(lài)的事件。如果讀者熟悉 Git 的一些概念,那么可以把它理解為 Git 中當(dāng)前所有分支的 HEAD 指針集合。

      2)添加事件與維護(hù) Frontier

      當(dāng)收到新事件時(shí),除了將事件存入當(dāng)前的事件表中,還需要更新 frontier。由于 frontier 記錄的是“沒(méi)有后續(xù)事件的事件”,當(dāng)新事件依賴(lài)某個(gè)舊 head 時(shí),說(shuō)明這個(gè)舊 head 已經(jīng)有了后續(xù),不再是“最新的”了,需要從 frontier 中移除,然后把新事件加入 frontier。

      fn EventGraph::add_event(self : EventGraph, event : Event) -> Unit {
      self.events[event.id] = event
      self.frontiers = self.heads.retain(frontier => !event.deps.contains(frontier))
      self.frontiers.push(event.id)
      }
      3)LCA(最近公共祖先) 與拓?fù)渑判?/p>

      合并兩個(gè)版本需要解決兩個(gè)問(wèn)題:

      1、找到分叉點(diǎn)(在 Event Graph 上找到 LCA ) :確定從哪里開(kāi)始重放

      2、確定重放順序(根據(jù) Lamport 拓?fù)渑判?) :按因果關(guān)系排序事件

      這兩部分都屬于比較基本的圖論問(wèn)題,相信讀者在查閱資料后可以很快的實(shí)現(xiàn)出來(lái)。不過(guò)需要注意的是,在工業(yè)實(shí)現(xiàn) Eg-Walker 算法時(shí),我們通常不使用常規(guī)介紹的算法求 LCA,而是對(duì)數(shù)據(jù)結(jié)構(gòu)進(jìn)行改進(jìn),應(yīng)用一些緩存機(jī)制來(lái)提高效率。

      4)合并算法

      現(xiàn)在我們有了所有組件,可以實(shí)現(xiàn)完整的合并算法了:

      fn EventGraph::merge(
      self : EventGraph,
      local_frontiers : Array[ID],
      remote_frontiers : Array[ID], // 遠(yuǎn)程 peer 的 frontiers,隨事件一起發(fā)送
      remote_events : Array[Event]
      ) -> String {
      // 步驟 1:將遠(yuǎn)程事件添加到事件圖
      for event in remote_events {
      self.add_event(event)
      }
      // 步驟 2:找到本地版本和遠(yuǎn)程版本的 LCA(用 VersionVector 取交集)
      let lca = self.find_lca(local_frontiers, remote_frontiers)
      // 步驟 4:收集從 LCA 到兩個(gè)分支的所有事件
      let events_to_replay = self.collect_events_after(lca)
      // 步驟 5:按 Lamport 時(shí)間戳拓?fù)渑判?br/> let sorted = self.topological_sort(events_to_replay)
      // 步驟 6:創(chuàng)建臨時(shí) RGA,重放所有事件
      let temp_rga = RGA::new()
      for event in sorted {
      self.apply_to_rga(temp_rga, event)
      }
      // 步驟 7:返回最終文本,丟棄臨時(shí) RGA
      temp_rga.to_string()
      }

      合并流程可以總結(jié)為三個(gè)階段:

      • Retreat(回退) :找到 LCA,確定需要重放的事件范圍

      • Collect(收集) :收集兩個(gè)分支上的所有事件,按 Lamport 時(shí)間戳拓?fù)渑判?/p>

      • Advance(推進(jìn)) :創(chuàng)建臨時(shí) RGA,按順序重放所有事件,用 CRDT 解決沖突


      六.Lomo 與開(kāi)發(fā)一個(gè)協(xié)作文本編輯器
      1、什么是 Loro/Lomo

      Loro 是一個(gè)基于 Eg-walker 算法的高性能 CRDT 庫(kù),由 Rust 實(shí)現(xiàn)。它支持多種數(shù)據(jù)類(lèi)型(文本、列表、Map、可移動(dòng)列表、樹(shù)結(jié)構(gòu)等),提供豐富的協(xié)作功能,被用于構(gòu)建實(shí)時(shí)協(xié)作應(yīng)用。而 Lomo 是 Loro 的 MoonBit 移植版本,與 Loro Rust 版本保持二進(jìn)制兼容,這意味著用 lomo 生成的文檔可以被 Loro 讀取,反之亦然。

      Lomo 的核心 API 非常簡(jiǎn)潔:

      let doc = LoroDoc::new()
      doc.set_peer_id(1UL)
      // 獲取文本容器并編輯
      let text = doc.get_text("content")
      doc.text_insert(text, 0, "Hello, World!")
      doc.text_delete(text, 5, 2)
      // 導(dǎo)出更新(用于同步)
      let updates = doc.export_updates()
      // 另一個(gè) peer 導(dǎo)入更新
      let doc2 = LoroDoc::new()
      doc2.set_peer_id(2UL)
      doc2.import_updates(updates) // 兩邊內(nèi)容自動(dòng)同步

      2、做一個(gè)協(xié)同文本編輯器

      因?yàn)閰f(xié)同需求經(jīng)常發(fā)生在前端,因此 Loro 發(fā)行了 Wasm API 以保證前端也可以使用這一優(yōu)秀的 CRDTs 庫(kù)。但 Rust 編譯的 Wasm 體積偏大,而且難以根據(jù)用戶(hù)某一項(xiàng)單獨(dú)需求進(jìn)行 tree-sharking,因此成為很多前端開(kāi)發(fā)者使用 Loro 的痛點(diǎn)。

      但前端如果使用 MoonBit+Lomo 在 JavaScript 后端編寫(xiě),則編譯器只會(huì)按需編譯 API,最終編譯結(jié)果非常好。同時(shí),MoonBit 的 Wasm 編譯結(jié)果往往會(huì)更小、更干凈,就算是使用 Wasm 后端進(jìn)行發(fā)行也會(huì)得到很好的效果。

      因此我們可以嘗試根據(jù) JavaScript 后端制作一個(gè)協(xié)同文本編輯器來(lái)驗(yàn)證這一點(diǎn),下面展示了大致的實(shí)現(xiàn)方式:

      首先在 MoonBit 一側(cè)封裝文檔操作,供 JavaScript 調(diào)用:

      ///| 創(chuàng)建文檔
      pub fn create_doc(peer_id : Int) -> Int {
      let doc = LoroDoc::new()
      doc.set_peer_id(peer_id.to_uint64())
      let text = doc.get_text("body")
      // 訂閱本地更新,自動(dòng)收集待發(fā)送的數(shù)據(jù)
      let _ = doc.subscribe_local_update((bytes) => {
      pending_updates.push(bytes)
      true
      }
      // ...
      }


      ///| 應(yīng)用編輯操作
      pub fn apply_edit_utf16(doc_id : Int, start : Int, delete_len : Int, insert_text : String) -> Bool {
      let doc = docs[doc_id]
      let text = texts[doc_id]
      if delete_len > 0 { doc.text_delete_utf16(text, start, delete_len)? }
      if insert_text.length() > 0 { doc.text_insert_utf16(text, start, insert_text)? }
      true
      }

      JavaScript 側(cè)處理用戶(hù)輸入和同步邏輯:

      // 處理用戶(hù)輸入
      function handleInput(side, other) {
      const nextText = side.el.textContent;
      const change = diffText(side.text, nextText); // 計(jì)算新舊文本的差異
      // 應(yīng)用到 CRDT(調(diào)用 MoonBit 導(dǎo)出的函數(shù))
      apply_edit_utf16(side.id, change.start, change.deleteCount, change.insertText);
      side.text = nextText;
      syncFrom(side, other); // 同步給另一方
      }


      // 同步邏輯
      function syncFrom(from, to) {
      const updates = drain_updates(from.id); // 獲取待發(fā)送的更新(MoonBit 導(dǎo)出)
      if (state.online) {
      apply_updates(to.id, updates); // 在線:立即應(yīng)用(MoonBit 導(dǎo)出)
      } else {
      from.outbox.push(...updates); // 離線:緩存到發(fā)件箱
      }
      }

      最終經(jīng)過(guò)一些樣式編寫(xiě)和頁(yè)面編寫(xiě)的工作,我們就可以得到一個(gè)基于 CRDTs 的協(xié)同編輯器:


      該項(xiàng)目的源碼在文章末尾已經(jīng)給出,感興趣的讀者可以自行參考并開(kāi)發(fā)更有意思的項(xiàng)目。

      七.總結(jié)

      本文從并發(fā)沖突問(wèn)題出發(fā),介紹了實(shí)時(shí)協(xié)作算法的演進(jìn):

      • OT :通過(guò)轉(zhuǎn)換操作解決沖突,但需要中央服務(wù)器

      • RGA :用唯一 ID 和相對(duì)位置實(shí)現(xiàn)去中心化,但元數(shù)據(jù)膨脹

      • Eg-walker:結(jié)合兩者優(yōu)點(diǎn),存儲(chǔ)簡(jiǎn)單操作,合并時(shí)臨時(shí)構(gòu)建 CRDT

      我們用 MoonBit 實(shí)現(xiàn)了上述算法的核心數(shù)據(jù)結(jié)構(gòu)與關(guān)鍵計(jì)算部分、還介紹了 Loro/lomo 庫(kù)和他們的基本使用,并使用 Lomo 開(kāi)發(fā)了一個(gè)簡(jiǎn)單的協(xié)作編輯應(yīng)用。

      從 1989 年 OT 的誕生,到 2011 年 RGA 等 CRDT 的形式化,再到 2024 年 Eg-walker 的創(chuàng)新融合,實(shí)時(shí)協(xié)作算法經(jīng)歷了三十余年的演進(jìn)。而近年來(lái)隨著 Local-first 理念的興起,CRDT 正從學(xué)術(shù)論文走向生產(chǎn)實(shí)踐——Figma、Linear 背后都有它的身影。

      未來(lái),歷史壓縮、復(fù)雜數(shù)據(jù)結(jié)構(gòu)、端到端加密等方向仍在快速推進(jìn);MoonBit 高效編譯到 WebAssembly 的能力,也為 CRDTs 在瀏覽器和邊緣設(shè)備上的部署提供了新可能。

      八.參考項(xiàng)目/文獻(xiàn)

      Lomo-Demo(編輯器)演示:

      https://lampese.github.io/lomo-demo/

      Lomo-Demo(編輯器)源碼:

      https://github.com/Lampese/lomo-demo

      Loro:https://loro.dev/

      Lomo:https://github.com/Lampese/lomo

      Eg-walker 論文:

      https://arxiv.org/abs/2409.14252

      聲明:包含AI生成內(nèi)容

      特別聲明:以上內(nèi)容(如有圖片或視頻亦包括在內(nèi))為自媒體平臺(tái)“網(wǎng)易號(hào)”用戶(hù)上傳并發(fā)布,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。

      Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

      相關(guān)推薦
      熱點(diǎn)推薦
      哈登!骨折?騎士接下來(lái)怎么辦?

      哈登!骨折?騎士接下來(lái)怎么辦?

      籃球盛世
      2026-02-26 10:40:39
      男子春節(jié)前將牛肉飯遺忘在辦公室,返工后發(fā)現(xiàn)其長(zhǎng)出15厘米“黑色叢林”!

      男子春節(jié)前將牛肉飯遺忘在辦公室,返工后發(fā)現(xiàn)其長(zhǎng)出15厘米“黑色叢林”!

      上觀新聞
      2026-02-26 17:19:08
      美貿(mào)易代表:特朗普改主意了,除中國(guó)之外,其他國(guó)家一個(gè)都跑不掉

      美貿(mào)易代表:特朗普改主意了,除中國(guó)之外,其他國(guó)家一個(gè)都跑不掉

      東極妙嚴(yán)
      2026-02-26 14:30:24
      回顧“91女神”琪琪:五官出眾,卻因天真讓自己“受傷”

      回顧“91女神”琪琪:五官出眾,卻因天真讓自己“受傷”

      就一點(diǎn)
      2025-11-22 10:36:39
      丁俊暉的商業(yè)帝國(guó):3600萬(wàn)獎(jiǎng)金根本不值一提

      丁俊暉的商業(yè)帝國(guó):3600萬(wàn)獎(jiǎng)金根本不值一提

      寶哥精彩賽事
      2026-02-26 18:26:59
      10球3助攻!曝麥克托米奈無(wú)緣回歸曼聯(lián),意甲MVP將加薪續(xù)約留隊(duì)

      10球3助攻!曝麥克托米奈無(wú)緣回歸曼聯(lián),意甲MVP將加薪續(xù)約留隊(duì)

      夏侯看英超
      2026-02-27 03:10:38
      黃曉明被爆新戀情,再次拿下“大眼睛芭比”美女,疑似新人歌手

      黃曉明被爆新戀情,再次拿下“大眼睛芭比”美女,疑似新人歌手

      她時(shí)尚丫
      2026-02-26 16:03:09
      “當(dāng)心砸了你兒子的飯碗”,無(wú)知母親曬公務(wù)員兒子做農(nóng)活,被群嘲

      “當(dāng)心砸了你兒子的飯碗”,無(wú)知母親曬公務(wù)員兒子做農(nóng)活,被群嘲

      妍妍教育日記
      2026-02-24 18:13:37
      伊朗退了,敘利亞退了,巴勒斯坦退了,黎巴嫩退了,塞爾維亞退了

      伊朗退了,敘利亞退了,巴勒斯坦退了,黎巴嫩退了,塞爾維亞退了

      南權(quán)先生
      2026-01-29 15:57:27
      豪門(mén)手段太高!向華強(qiáng)公開(kāi)表態(tài)財(cái)產(chǎn)留給兒媳婦,向佐別想搞外遇

      豪門(mén)手段太高!向華強(qiáng)公開(kāi)表態(tài)財(cái)產(chǎn)留給兒媳婦,向佐別想搞外遇

      萌神木木
      2026-02-26 13:03:02
      呼吸科主任提醒:馬上停止食用4類(lèi)食物,吃得越久,肺結(jié)節(jié)越長(zhǎng)

      呼吸科主任提醒:馬上停止食用4類(lèi)食物,吃得越久,肺結(jié)節(jié)越長(zhǎng)

      岐黃傳人孫大夫
      2026-02-26 22:10:03
      砒霜中毒怎么辦,只需一味中藥即可解,它即是食物,同時(shí)也是中藥

      砒霜中毒怎么辦,只需一味中藥即可解,它即是食物,同時(shí)也是中藥

      環(huán)京快爆
      2026-02-26 11:02:14
      同樣煮餃子,“蓋蓋煮”和“不蓋蓋煮”區(qū)別大,難怪煮出來(lái)不一樣

      同樣煮餃子,“蓋蓋煮”和“不蓋蓋煮”區(qū)別大,難怪煮出來(lái)不一樣

      阿龍美食記
      2026-02-23 17:00:18
      公立醫(yī)院買(mǎi)不著進(jìn)口藥?不是消失,是換地方了!

      公立醫(yī)院買(mǎi)不著進(jìn)口藥?不是消失,是換地方了!

      今日養(yǎng)生之道
      2026-02-26 17:26:32
      人社部傳來(lái)好消息!2026年養(yǎng)老金或繼續(xù)調(diào)整,企退人員能漲4%嗎?

      人社部傳來(lái)好消息!2026年養(yǎng)老金或繼續(xù)調(diào)整,企退人員能漲4%嗎?

      另子維愛(ài)讀史
      2026-02-26 20:19:13
      WOC!哈登!麻了,騎士心碎了...

      WOC!哈登!麻了,騎士心碎了...

      技巧君侃球
      2026-02-26 15:18:22
      這些“萬(wàn)億地級(jí)市”,增長(zhǎng)密碼是什么?

      這些“萬(wàn)億地級(jí)市”,增長(zhǎng)密碼是什么?

      新華社
      2026-02-25 17:33:37
      前曼聯(lián)助教談球隊(duì)沒(méi)有簽下奧斯梅恩的原因;卡里克:看到庫(kù)尼亞,讓我想起特維斯

      前曼聯(lián)助教談球隊(duì)沒(méi)有簽下奧斯梅恩的原因;卡里克:看到庫(kù)尼亞,讓我想起特維斯

      MUREDS
      2026-02-26 23:52:02
      松島輝空談出局:很多人都被吹了TTR,球也不是我擅長(zhǎng)的類(lèi)型

      松島輝空談出局:很多人都被吹了TTR,球也不是我擅長(zhǎng)的類(lèi)型

      懂球帝
      2026-02-26 20:54:03
      事實(shí)證明,人一輩子最艱難的,就是50歲到60歲這十年,原因有6點(diǎn)

      事實(shí)證明,人一輩子最艱難的,就是50歲到60歲這十年,原因有6點(diǎn)

      健康之光
      2026-02-26 19:20:03
      2026-02-27 05:19:00
      開(kāi)源中國(guó) incentive-icons
      開(kāi)源中國(guó)
      每天為開(kāi)發(fā)者推送最新技術(shù)資訊
      7600文章數(shù) 34502關(guān)注度
      往期回顧 全部

      科技要聞

      單季營(yíng)收681億凈利429億!英偉達(dá)再次炸裂

      頭條要聞

      美國(guó)政府對(duì)外交官下令:開(kāi)始行動(dòng)

      頭條要聞

      美國(guó)政府對(duì)外交官下令:開(kāi)始行動(dòng)

      體育要聞

      從排球少女到冰壺女神,她在米蘭冬奧練出6塊腹肌

      娛樂(lè)要聞

      向華強(qiáng)公開(kāi)表態(tài) 財(cái)產(chǎn)留給兒媳婦郭碧婷

      財(cái)經(jīng)要聞

      中國(guó)AI調(diào)用量超美國(guó) 4款大模型霸榜前5

      汽車(chē)要聞

      40歲的吉利,不惑于內(nèi)外

      態(tài)度原創(chuàng)

      數(shù)碼
      時(shí)尚
      房產(chǎn)
      公開(kāi)課
      軍事航空

      數(shù)碼要聞

      三星Galaxy S26全球新品發(fā)布

      今年春天最美搭配:西裝+半裙,怎么穿都好看!

      房產(chǎn)要聞

      2.2萬(wàn)/m2起!三亞主城性?xún)r(jià)比標(biāo)桿 海墾·桃花源實(shí)景現(xiàn)房春節(jié)被瘋搶

      公開(kāi)課

      李玫瑾:為什么性格比能力更重要?

      軍事要聞

      美政府給新伊核協(xié)議設(shè)限內(nèi)容遭披露

      無(wú)障礙瀏覽 進(jìn)入關(guān)懷版