webman需自行集成websocket+ot/crdt实现协同编辑:启用独立websocketworker、分层redis存储(snapshot/oplog/cursor)、ime输入延迟至compositionend提交、断连后基于last_seq重放oplog。

Webman 本身不内置协同编辑能力,得自己搭 WebSocket + OT/CRDT
Webman 是一个基于 Workerman 的 PHP HTTP 框架,定位是「高性能 Web 服务」,不是实时协作框架。它没有开箱即用的协同编辑逻辑,也不提供 Operational Transformation(OT)或 CRDT 等冲突解决算法。想用 Webman 做多人在线文档编辑,必须手动集成底层通信和状态同步机制。
常见误区是以为引入 webman-core 或写个 CollaborationController 就能支持实时协同——实际连光标位置同步都做不到。HTTP 是无状态、短连接,而协同编辑依赖低延迟、双向、持久的消息通道。
- 必须启用 WebSocket 服务(例如通过
workerman/websocket-server扩展) - 需自行实现文档状态管理(如用 Redis 存 document snapshot + operation log)
- 客户端发送的每次输入、删除、选区变化,都要序列化为可合并的操作(
insert、delete、retain),不能直接传 HTML 或富文本字符串 - 服务端收到操作后,要按客户端版本号做 transform 再广播,否则会出现「A 插入后 B 删除错位置」这类错乱
WebSocket 连接必须脱离 Webman HTTP 生命周期独立运行
Webman 的 HttpWorker 处理的是请求-响应模型,无法维持长连接。若强行在 onMessage 里启动 WebSocket,会因进程回收导致连接频繁断开,表现为「编辑中突然掉线」「光标消失」「内容回滚」。
正确做法是:用 Workerman 启动一个独立的 WebSocketWorker,与 Webman 的 HttpWorker 共享同一份配置和存储(如 Redis),但完全解耦生命周期。
- 不要在
app/controller里 new WebSocketServer - WebSocket 服务应监听单独端口(如
ws://localhost:2120),而非复用 Webman 的 8787 - 用户登录后,前端先调 Webman 接口获取 token 和 docId,再用该信息连接 WebSocket 服务
- 连接建立后,所有编辑事件走 WebSocket,HTTP 接口仅用于初始化加载、权限校验、快照保存等非实时操作
中文输入法场景下,operation 必须延迟提交直到 compositionend
直接监听 input 事件并立即发 operation,会导致中文输入中途被其他协作者干扰:比如你在打「你好」,还没选词完成,对方插入一段文字,你的「你」字可能被错位插入到别处,甚至触发异常 DOM diff。
Webman 2.2.0版本强化了 TCP/UDP 服务支持,优化路由组管理,并增强异步任务处理能力。结合协程与连接池技术,Webman 能轻松应对高并发场景,适用于网站、接口服务、即时通讯、物联网及游戏开发,兼具高性能、灵活扩展与稳定可靠,是多场景 PHP 服务开发的理想选择。
浏览器对 IME 输入有明确事件流:compositionstart → input(未确认)→ compositionend(确认上屏)。只有在 compositionend 后,才应把整段已确认文本封装为一个 insert operation 广播出去。
- 在富文本编辑器(如 Quill、Tiptap)中,需 patch
handleInput或监听原生事件,拦截未完成的input - 服务端收到带
isComposing: true标记的操作,应暂存队列,不参与 transform,直到收到同 client 的compositionend指令才合并执行 - 若用户长时间卡在输入法状态(如网络抖动),需加 3s 超时兜底,避免操作积压
Redis 存储结构设计直接影响并发安全和恢复体验
很多人用 SET doc:123 粗暴存快照,这在协同场景下极危险:两个用户同时保存,后写入者会覆盖前者的全部变更,且无法追溯谁改了哪一行。
推荐分层存储:
-
doc:123:snapshot:只存最终一致的 HTML 或 JSON-AST,供新用户首次加载 -
doc:123:oplog:用 Redis Stream 存操作日志,每条含client_id、seq、op、ts,天然支持按时间回放 -
doc:123:cursor:Hash 结构存各在线用户的光标位置(user_a → {line:5, col:12}),用于服务端校验编辑冲突 - 每次操作前,用
WATCH doc:123:oplog+MULTI保证原子写入,避免并发 append 错序
最易被忽略的一点:WebSocket 连接断开重连时,不能只拉最新 snapshot ——必须从断连时刻的 last_seq 开始 replay oplog,否则会丢失中间所有变更。这个 last_seq 必须由客户端在断连前主动上报,或服务端用 Redis ZSET 维护每个 client 的已处理序号。










