Webman协作文档后端核心是避免状态错乱:WebSocket不可用$_SESSION,须用token绑定user_id;文档必须带version字段实现乐观锁;HTTP与WebSocket须共享DocumentStore统一状态。

Webman 构建在线协作文档后端,核心不是“实时同步有多快”,而是「如何避免状态错乱」——WebSocket 连接不共享 session、静态变量在常驻进程中跨请求污染、JSON Patch 或 OT 操作没做并发校验,这三个点踩中任意一个,协作就会出现内容覆盖或版本回滚。
WebSocket 连接里不能用 $_SESSION
Webman 的 WebSocket 服务运行在独立进程,不经过 PHP-FPM,$_SESSION 完全不可用。常见错误是直接在 onMessage 里调 session_start() 或读 $_SESSION['user_id'],结果永远为空。
- 改用 token 校验:前端连接时在 query string 带
?token=xxx,服务端用$connection->getRemoteAddress()+$connection->getArgs()提取并验证 JWT - 把用户身份存到连接对象上:
$connection->user_id = $uid;,后续所有操作都基于这个字段做权限和隔离 - 别把用户信息塞进
self::$docs数组键名(如self::$docs["{$doc_id}_{$user_id}"]),容易内存泄漏;应统一用文档 ID 隔离,权限由操作时校验
文档状态必须带 version 字段做乐观锁
多个客户端同时编辑同一文档时,靠“最后写入获胜”会丢数据。self::$docs[$doc_id]['version'] 不是可选优化项,是必需字段。
Webman 2.2.0版本强化了 TCP/UDP 服务支持,优化路由组管理,并增强异步任务处理能力。结合协程与连接池技术,Webman 能轻松应对高并发场景,适用于网站、接口服务、即时通讯、物联网及游戏开发,兼具高性能、灵活扩展与稳定可靠,是多场景 PHP 服务开发的理想选择。
- 每次
onMessage收到操作前,先比对客户端传来的version和当前服务端self::$docs[$doc_id]['version'] - 不一致就立刻返回
{ "type": "rebase", "snapshot": "...", "version": N },强制前端拉取最新快照重放操作 - 成功应用操作后,必须原子递增
self::$docs[$doc_id]['version']++,不能用+= 1(协程环境下可能丢失) - 不要用时间戳替代 version —— 高并发下毫秒级时间戳会重复,无法保证单调递增
HTTP 接口与 WebSocket 必须共享同一份文档存储
用户通过 HTTP GET /api/doc/123 查看文档,和通过 WebSocket 编辑同一份文档,如果两者读写不同变量或缓存,必然出现数据不一致。
- 把
self::$docs抽成独立的 DocumentStore 类,提供get($id)、update($id, $ops, $version)等方法,HTTP 控制器和 WebSocket 回调都调它 - DocumentStore 内部用
Swoole\Table替代普通数组,支持多进程安全读写(Webman 默认启用多 worker 进程) - HTTP 接口返回文档时,必须带上当前
version字段,前端保存用于下次 WebSocket 操作校验 - 禁止在 HTTP 接口里直接
file_get_contents("doc_123.json")—— 文件 I/O 会阻塞协程,且无法与 WebSocket 内存状态同步
真正难的不是实现“谁改了哪一行”,而是让 HTTP 请求、WebSocket 消息、定时自动保存、甚至离线补发操作,全部落在同一个状态机上。version 字段漏校验、存储没抽象、连接身份没绑定——这三处一旦松动,协作就退化成单人编辑器。

















