技术方案 · V1.0

多人在线协同编辑 Markdown 文件方案

基于 WebSocket + CRDT 的实时协同编辑能力设计,支持多人同时编辑、冲突自动合并、光标实时同步与细粒度权限控制
文档类型:方案设计 适用范围:网页端 / 桌面端 日期:2026 年 9 月

目录

  1. 背景与目标
  2. 核心挑战分析
  3. 总体架构设计
  4. 冲突处理与算法选型
  5. 核心模块详细设计
  6. 技术选型与对比
  7. 关键流程时序
  8. 部署与容量规划
  9. 实施计划
1

背景与目标

为什么需要协同编辑,以及本方案要达成的目标

1.1 背景

当前团队基于 Markdown 进行知识沉淀(方案文档、周报、会议纪要、知识库条目等),文件以本地或单点存储为主,存在以下痛点:

1.2 目标

目标说明
实时协同多人同时编辑同一 Markdown 文件,内容变更端到端延迟控制在 300ms 以内(同机房)
无感合并并发编辑自动合并,无需人工处理冲突;保证所有客户端最终一致
在线状态实时展示协作者头像、光标位置、选区范围;支持关注(Follow)模式
权限可控支持只读 / 评论 / 可编辑三级权限,按文档(后续可扩展到段落级)授权
历史可溯保留操作级历史,支持任意时刻快照回滚与按人的变更回放
非目标(本期不做) 不做富文本块编辑器(保持纯 Markdown 语法语义);不做离线编辑(要求在线使用);不做跨文档引用渲染(仅渲染本文件内容)。
2

核心挑战分析

协同编辑的本质难题是"并发"——必须先想清楚难在哪,再谈方案
同一份 Markdown 文档 用户 A · 插入标题 用户 B · 修改列表项 用户 C · 删除段落 如何无冲突地合并?
图 2-1 并发编辑冲突示意:多端操作同时作用于同一文档
3

总体架构设计

客户端编辑器 + WebSocket 长连接 + 协同服务 + 持久化存储的四层结构
客户端层(浏览器 / Electron) 编辑器内核 CodeMirror 6 + Yjs Binding 协同状态管理 Awareness(光标/在线状态) 双栏渲染 编辑视图 ↔ 预览视图 WebSocket 长连接(二进制协议) 协同服务层(Node.js) 房间网关 连接管理 · 鉴权 · 路由 CRDT 同步引擎 Yjs y-websocket 协议 持久化调度 防抖快照 · 快照恢复 Redis 房间注册 · 跨节点广播 存储层 文档快照存储 Yjs Update 增量 + 定期合并快照 元数据 / 权限库(MySQL) 文档 · 成员 · 权限 · 版本记录 对象存储(可选) 文档内图片等附件
图 3-1 系统总体架构:客户端 → 协同服务 → 存储三层结构

3.1 分层职责

层次职责关键技术
客户端层Markdown 编辑与渲染、本地 CRDT 文档状态、光标/在线状态广播、断线缓存重放CodeMirror 6、Yjs、ProseMirror 可选
协同服务层文档房间管理、消息转发、增量合并与广播、防抖持久化、权限校验Node.js、ws、y-websocket、Redis Pub/Sub
存储层增量更新存储、定期快照合并、版本历史、元数据与权限、附件MySQL、对象存储、Redis

3.2 房间(Room)模型

每个打开中的 Markdown 文件对应一个协同房间:

4

冲突处理与算法选型

OT 与 CRDT 两条主流路线的对比与本方案的取舍
维度OT(Operational Transformation)CRDT(如 Yjs)
合并职责依赖中心服务器做变换,客户端逻辑简单各端基于本地操作独立收敛,服务器仅转发
实现复杂度服务器变换函数难以写对,历史缺陷多算法由成熟库封装(Yjs),业务侧开发量小
离线/弱网依赖稳定的服务器序列,离线支持复杂天然支持:本地先生成 op,上线后自动合并
光标同步需另建坐标变换机制Relative Position 原生支持,与文档同源
性能大文档、长历史时变换开销增长明显Yjs 增量编码 + 快照压缩,实测百万字符级可用
典型代表Google Docs(自研)、ShareDBYjs、Automerge、Loro
选型结论:采用 CRDT 路线,具体使用 Yjs ① 我们的场景是中小团队文档协作,不追求 Google Docs 级自研 OT;② Yjs 与 CodeMirror 6 有官方成熟绑定,接入成本低;③ 服务器只做"转发 + 存储",复杂度低、易水平扩展;④ 增量协议天然支持断线追平与历史回放。

4.1 Yjs 工作原理(简要)

4.2 并发场景行为约定

并发场景合并结果
两人同时在不同位置插入文本两处插入均保留,按确定顺序排列
同一位置并发插入按客户端 ID 决定先后,结果稳定一致
一人编辑、一人删除同一段落删除生效(删除优先),编辑内容保留在墓碑层,历史中可查
Markdown 语法结构(列表、表格)按纯文本行处理;行级整体移动为"删 + 插",不保证特殊结构语义合并
注意 本方案将 Markdown 视为纯文本进行 CRDT 同步(Y.Text),不做块级(Y.Map / Y.Array)结构化建模。优点是接入简单、与现有纯 Markdown 文件完全兼容;代价是列表/表格的结构语义合并较弱。若未来演进为块编辑器,可切换 Yjs 嵌套类型(Y.XmlFragment),本方案架构不变。
5

核心模块详细设计

编辑器、同步协议、在线状态、权限、历史五个模块的设计要点

5.1 编辑器模块(客户端)

子项设计
编辑内核CodeMirror 6:轻量、插件体系清晰,官方提供 y-codemirror.next 绑定,自动完成"本地编辑 → Yjs Update → 远端回放"闭环
双栏视图左栏 Markdown 源码编辑、右栏实时渲染预览;渲染层可跟随光标滚动同步
远端光标通过 yCollab 的 Awareness 能力,为每个协作者渲染带名字标签的彩色光标与选区;颜色按 userId 哈希固定
本地缓冲Yjs Update 先写入本地内存并即时上屏,网络失败进入待发队列,重连后按序补发——保证"本地永不丢键"

5.2 同步协议(y-websocket)

采用 Yjs 官方 y-websocket 二进制协议,消息类型:同步请求(SyncStep1/2)、增量更新(Update)、在线状态(Awareness)。

客户端接入流程:
1. WS 连接 /ws/doc/:docId?token=xxx
2. 服务端校验 token 与文档权限 → 绑定用户与房间
3. 双方互发 SyncStep1(各自持有状态向量 SV)
4. 对方按差集回传 SyncStep2(缺失的增量 Update)
5. 此后本地编辑实时广播 Update;Awareness 心跳 15s/次

5.3 在线状态模块(Awareness)

信息说明
用户身份userId、昵称、头像 URL、主题色
光标 / 选区Yjs Relative Position 锚点,随文档演进自动重定位
视图状态正在编辑 / 正在预览 / 空闲(超过 60s 无操作标记为空闲)

顶部协作栏实时展示在线成员头像列表;提供「关注某人」能力——跟随目标用户的光标与滚动位置,便于结对评审与讲解。

5.4 权限模块

角色能力实现方式
只读查看内容与在线成员WS 连接标记只读,服务端拒绝其 Update 消息;客户端编辑器置为 readOnly
评论只读 + 行级评论评论走独立 HTTP 接口与数据表,不进入 CRDT 文档
可编辑全部能力正常参与协同

5.5 历史版本模块

5.6 持久化策略

落盘时机(防抖 + 兜底):
- 编辑静默 3 秒后,将服务端房间 Y.Doc 当前状态编码落盘
- 最后一名用户离开房间时强制落盘并回收
- 每 5 分钟定时快照一次(兜底)
- 服务优雅停机前 flush 全部活跃房间

恢复路径:启动加载最新快照 → 重放快照之后的增量 Update → 得到完整文档状态。

6

技术选型与对比

各关键组件的候选对比与最终选择
组件候选方案最终选择理由
CRDT 库Yjs / Automerge / LoroYjs 生态最成熟(编辑器绑定齐全)、性能标杆、二进制 Update 紧凑
编辑器CodeMirror 6 / Monaco / ProseMirrorCodeMirror 6 专为轻量源码/文本设计,体积小、移动端表现好,Yjs 官方绑定
通信WebSocket / SSE / WebRTCWebSocket 双向低延迟;SSE 单向不适合;WebRTC 仅在超低延迟场景有必要
服务端Node.js / Go / JavaNode.js y-websocket 官方实现开箱即用;I/O 密集型场景表现好
渲染marked / markdown-it / unifiedmarkdown-it CommonMark 兼容好、插件生态丰富、可扩展代码高亮与任务列表
自研 vs 集成现成产品 若允许引入第三方,可评估飞书文档、语雀、Notion 等托管能力(快、稳,但数据不出自己体系、Markdown 兼容受限)。本方案定位自建轻量协同能力:核心链路基于开源组件拼装(y-websocket + CodeMirror),自研部分集中在权限、历史与业务集成,预计核心功能 3~4 周可交付 MVP。
7

关键流程时序

一次典型协同编辑的端到端流程
用户 A(编辑) 协同服务 用户 B(编辑) 1. WS 连接 + 鉴权 2. WS 连接 + 鉴权 3. 全量同步(当前文档状态) 4. 全量同步 5. A 键入 → 广播 Update 6. 转发给房间内其他人 7. B 并发编辑 → 广播 Update 8. 转发给 A(CRDT 自动合并) 9. 防抖落盘 快照存储 增量 + 定期合并 所有端 A、B 看到完全一致的最终文档(收敛)
图 7-1 协同编辑核心时序:接入 → 同步 → 并发编辑广播 → 落盘

7.1 断线重连流程

8

部署与容量规划

从单机到集群的演进路径
阶段形态要点
MVP单机部署:1 个 Node 进程 + MySQL + Redis房间全在单进程内,无需跨节点广播;预计支撑 50+ 并发房间、单房间 20 人
水平扩展多进程 / 多机 + Redis Pub/Sub同一房间由一致性哈希路由到固定节点;Redis Pub/Sub 负责跨节点的 Awareness 与控制消息广播
高可用Nginx/网关负载均衡 + 健康检查节点故障时房间在其他节点从快照重建;WS 网关配置 sticky session

8.1 容量估算(MVP 假设)

8.2 安全与运维

9

实施计划

三个阶段递进交付,每阶段均有可演示产出
阶段周期交付内容里程碑验收标准
一期 · MVP第 1~3 周 编辑器双栏界面、y-websocket 协同服务、多端实时同步、远端光标、基本落盘与恢复 两浏览器并发编辑完全一致;刷新/重启服务后内容不丢
二期 · 协作增强第 4~5 周 三级权限、行级评论、历史版本查看与回滚、断线重连体验优化 权限变更实时生效;可回放任意历史时刻并安全回滚
三期 · 工程化第 6~8 周 多节点扩展(Redis Pub/Sub)、监控告警接入、压测与调优、与知识库/现有系统集成 单房间 50 人压测 P95 延迟 < 500ms;服务重启无数据丢失

9.1 风险与对策

风险影响对策
纯文本 CRDT 对 Markdown 结构合并较弱列表/表格并发编辑语义不理想一期按纯文本交付并在文档中说明边界;三期评估 Y.XmlFragment 块级建模升级
大文档性能(>100KB)首屏同步慢、内存占用高压测验证;必要时按章节分段加载 / 虚拟滚动
多人高频编辑消息风暴延迟抖动客户端微批 + 服务端合流广播;限流兜底
与现有文件存储体系不一致协同态与磁盘文件状态出现分歧以协同文档为唯一真源,落盘单向写回 Markdown 文件;避免双写冲突
方案小结Yjs(CRDT)+ CodeMirror 6 + Node WebSocket 服务为核心搭建轻量协同编辑能力:算法层保证并发自动合并与最终一致,架构层以"房间 + 增量同步 + 防抖快照"支撑多人文档协作,业务层叠加权限、评论、历史三大协作要素。整体基于成熟开源组件,自研范围可控,8 周内可完成三阶段交付。