Files
cc-web/docs/WEBSOCKET-TRANSPORT-EVALUATION.md
2026-09-14 22:03:23 +08:00

198 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# cc-web WebSocket 替换为 SSE / WebTransport 评估报告
日期:2026-09-14
## 结论
当前项目不适合直接把 WebSocket 整体替换成 SSE 或 WebTransport。
推荐保留 WebSocket 作为默认双向控制通道,同时增加“认证后的 SSE 下行通道 + HTTP
命令接口”的渐进式方案。这样可以先降低代理对 WebSocket 的依赖,并保留现有协议和
回退能力。WebTransport 适合后续独立 PoC,不建议作为近期生产替换目标。
| 方案 | 当前项目可行性 | 适合的范围 | 主要结论 |
|---|---:|---|---|
| 继续使用 WebSocket | 高 | 现有全部功能 | 成本最低,协议已覆盖双向消息、审批和实时输出 |
| SSE + HTTP 命令 | 高(渐进式) | 服务端事件下行、流式输出 | 推荐;需要事件总线、重放和认证改造 |
| 纯 SSE 替换 | 中低 | 只读监控或单向推送 | 不匹配当前大量客户端命令和交互式请求 |
| WebTransport | 中低(近期),高(专用场景) | 高并发低延迟、可靠流+不可靠数据报 | 基础设施和 Node 服务端生态成本过高 |
## 当前 WebSocket 的职责
项目的 WebSocket 并非只用来传输模型文本,而是整个浏览器会话的双向 RPC 通道。
- `server.js:9330-9575` 创建 `/ws` 的 `WebSocketServer`,在首帧完成密码或 token
认证,然后按 JSON `type` 分发命令。
- 客户端在 `public/app.js:7781-7870` 建立连接、解析 JSON、指数退避重连,并在
`public/app.js:7191-7204` 等位置通过同一连接发送命令。
- 普通 Claude/Codex 运行时在 `lib/agent-runtime.js:359-590` 产生
`text_delta`、`content_blocks`、`tool_start/update/end`、`usage` 等事件;Codex
App 在 `lib/codex-app-runtime.js:437` 复用相同下行抽象。
- 服务端还按会话查看关系发送事件(`server.js:6835-6851`),并向所有认证客户端
广播任务看板和后台完成事件(`server.js:6512-6529`、`server.js:6827-6831`)。
- `activeProcesses`、`activeCodexAppTurns` 和 `wsSessionMap`
(`server.js:1420-1503`)把运行中的进程、当前会话和连接绑定在内存中。断线时
`handleDisconnect`(`server.js:11397-11425`)解绑连接,但进程继续运行,重连后再
恢复查看。
- 客户端和服务端都有心跳:服务端 WebSocket ping 在 `server.js:9578-9604`,客户端
应用层 heartbeat 在 `public/app.js:7720-7775`。这也是当前反向代理长连接稳定性的
一部分。
因此,替换传输层必须保留以下语义:实时增量、工具调用生命周期、审批/引导输入的
双向往返、会话切换与恢复、跨会话广播、断线重连、重复命令保护和后台任务通知。
## SSE 评估
### 可行性
SSE 很适合承载本项目的服务端下行事件。`text_delta`、工具状态、`done`、会话列表、
任务看板和提示事件都可以编码为带 `event`、`id`、`data` 的 SSE 帧。浏览器原生
`EventSource` 自带自动重连,服务端可用注释心跳保持连接。
但 SSE 只能由服务器向浏览器推送。当前客户端发送的 `message`、`abort`、会话管理、
设置、任务看板查询、审批响应和引导输入响应必须迁移到 HTTP `POST`/`PATCH`/`DELETE`
命令接口,或继续由 WebSocket 承担。这意味着“纯 SSE”不是小改动;“SSE 下行 + HTTP
命令”才是可行的替代架构。
### 优点
- 基于普通 HTTP,Nginx、Caddy、云负载均衡和审计工具更容易接入。
- 浏览器 API 简单,断线重连和 `Last-Event-ID` 已有标准语义。
- 事件天然是文本 JSON,与当前 `wsSend(JSON.stringify(data))` 的消息模型接近。
- 单向输出的代码边界清晰,适合模型流式文本、工具进度和只读监控。
- 不需要 UDP/443、QUIC、HTTP/3 或新的服务端运行时。
### 缺点和改造点
- 不能承载现有客户端到服务端的命令;需要新建命令路由、请求 ID、错误响应和幂等控制。
- 原生 `EventSource` 不能设置自定义 `Authorization` 头。当前 token 放在首个 WebSocket
JSON 帧中,迁移时应优先改为安全 Cookie(配套 CSRF 防护),或使用 `fetch` 流式读取;
把 token 放 URL 会进入代理日志、历史记录和监控标签,不建议。
- 当前仅保存在会话 JSON 和运行态文件中的状态不足以重放每个增量。必须为 SSE 事件分配
单调 `id`,并增加短期事件缓冲或按 `sessionId` 重发快照,否则网络抖动时会丢字、丢工具状态。
- HTTP/1.1 下浏览器对同源并发连接数有限;HTTP/2 可改善连接复用,但反向代理必须关闭
响应缓冲并提高读超时。Nginx 通常需要 `proxy_buffering off`、`X-Accel-Buffering: no`
和足够大的 `proxy_read_timeout`。
- 一条 SSE 流是有序可靠字节流;慢客户端会形成反压,需要限制队列、丢弃可重建事件或
断开慢连接,不能无限堆积内存。
- 水平扩展时,SSE 客户端和运行进程仍绑定单个 Node 实例;需要粘性会话或 Redis/NATS
等发布订阅与事件重放层。当前项目没有这层基础设施。
### 对当前项目的评分
| 指标 | SSE 下行 + HTTP 命令 |
|---|---:|
| 代码复用 | 7/10 |
| 浏览器兼容 | 9/10 |
| 代理/部署 | 8/10 |
| 双向交互适配 | 6/10 |
| 断线恢复 | 需新增 6/10 |
| 近期落地建议 | 推荐灰度 |
## WebTransport 评估
### 可行性
WebTransport 基于 HTTP/3/QUIC,同时提供可靠的双向流和可丢失的数据报,理论上可以
较完整地承接当前 WebSocket 的双向 JSON 协议。普通命令、审批和模型文本应使用可靠
双向流;只有明确允许丢失的高频状态才考虑数据报。
当前项目的 Node `http.createServer` + `ws` 结构没有现成 WebTransport 入口。引入后需要
HTTP/3/QUIC 服务端库或独立网关、证书和连接管理;现有 `/ws` 的升级、心跳、认证、
反向代理配置和运维监控均不能直接复用。
### 优点
- 原生双向通信,命令和事件不必拆成两套协议。
- QUIC 在多条流之间避免 TCP 层队头阻塞;建立连接和网络切换体验可能更好。
- 可按场景选择可靠流或低延迟数据报,适合未来高频协作光标、实时遥测等功能。
- 连接由 HTTP/3 承载,具备现代传输层的多路复用能力。
### 缺点和风险
- 浏览器必须处于安全上下文,服务端和代理必须支持 HTTP/3/QUIC;UDP/443、防火墙、
云负载均衡和企业网络放行都成为部署前置条件。
- Node 核心当前没有与 `ws` 同等成熟、可直接替换的稳定高层 WebTransport 服务端 API,
需要评估第三方库或独立网关的维护状态、内存安全和协议兼容性。
- 现有 Nginx/HTTP 反向代理配置按 HTTP/1.1 WebSocket 编写,不能假设能透明转发
WebTransport;需要逐个验证 HTTP/3 终止点、QUIC 到后端的转发方式和超时策略。
- 数据报不保证送达、顺序或不重复,不能承载文本增量、审批、abort、会话切换等关键
消息;可靠流仍需实现应用层消息边界、背压、重连和幂等。
- 连接迁移、连接 ID、TLS、HTTP/3 日志和指标与当前 WebSocket 运维经验不同,故障排查
成本明显更高。
- Safari、旧版浏览器、企业代理和受限网络的可用性需要实测,生产仍需 WebSocket/SSE
回退;这会带来三套客户端和协议测试矩阵。
### 对当前项目的评分
| 指标 | WebTransport |
|---|---:|
| 代码复用 | 5/10 |
| 浏览器兼容 | 5/10(需目标用户实测) |
| 代理/部署 | 3/10 |
| 双向交互适配 | 8/10 |
| 低延迟/多路复用潜力 | 9/10 |
| 近期落地建议 | 不推荐直接替换 |
## 改造规模与风险
| 领域 | SSE 方案 | WebTransport 方案 |
|---|---|---|
| 服务端 | 抽象 `wsSend` 为事件发布器;新增 SSE 连接、命令 API、事件 ID/重放 | 替换连接层、增加 HTTP/3/QUIC 服务端和可靠流协议 |
| 前端 | `EventSource`/fetch 流读取;把 `send()` 改为 HTTP 命令;保留统一消息处理器 | 新建 WebTransport 客户端、流帧协议、能力探测和多级回退 |
| 认证 | Cookie+CSRF 或 fetch 自定义头;处理连接失效 | QUIC 握手后应用认证、连接恢复和 token 轮换 |
| 恢复 | `Last-Event-ID`、事件缓冲、会话快照 | 连接迁移、流重建、消息幂等与快照 |
| 部署 | 代理关闭缓冲、长超时,HTTP/2 优先 | HTTP/3、UDP/443、证书、网关、监控和防火墙 |
| 测试 | 事件顺序、重放、慢客户端、代理超时、CSRF | 可靠/不可靠流、丢包、网络切换、浏览器和代理矩阵 |
| 估算 | 2–4 人周做灰度骨架,4–8 人周完成替换 | 6–12 人周 PoC,生产化通常更久,取决于网关和网络 |
上述估算不包含新增 Redis/NATS、HTTP/3 网关或大规模压测;多实例部署会增加工作量。
## 推荐实施路线
1. 先把 `wsSend`、`sendSessionEventToViewers`、全局广播和运行时 `sendRuntime` 收敛到
一个传输无关的事件发布接口,统一事件名、`sessionId`、`requestId`、时间戳和递增
`eventId`。保留现有 WebSocket 适配器,确保这一步行为不变。
2. 增加受保护的 `/api/events` SSE 端点,只接入只读会话列表、任务事件和运行时下行事件。
对每个连接限制队列,发送注释心跳,并支持 `Last-Event-ID` 后按会话重发快照。
3. 以 Cookie 或 `fetch` 流读取解决认证,不把长期 token 放入 URL;命令端点使用
`requestId` 和幂等键,返回 202 后由 SSE 回传结果。先迁移 `message`、`abort`、会话
切换和审批响应,再迁移设置与任务看板命令。
4. 对比 WebSocket 与 SSE 的首字延迟、完整回合延迟、断线恢复丢事件数、慢客户端内存、
代理超时和移动网络表现。通过特性开关按用户或会话灰度,WebSocket 保留为回退。
5. 只有在所有命令均有 HTTP 等价物、事件重放和多实例路由验证通过后,才考虑下线默认
WebSocket;机器人和内部集成需单独迁移,不能只看浏览器 UI。
6. 如确有 QUIC 需求,再建立独立 WebTransport PoC,先验证目标浏览器、TLS/HTTP3 网关、
UDP 网络、可靠流重连和监控,再决定是否替代 SSE/WS。
## 最终建议
对 cc-web 当前“模型流式输出 + 交互式审批 + 会话控制 + 单 Node 进程状态绑定”的
形态,优先级应为:
1. **近期:继续 WebSocket,先做传输无关事件层和协议整理。**
2. **中期:SSE 下行 + HTTP 命令灰度,逐步减少对 WebSocket 的依赖。**
3. **长期:只有在确认 HTTP/3/QUIC 基础设施和用户浏览器覆盖后,才评估 WebTransport。**
直接替换为 SSE 会把双向协议问题转移到大量 HTTP 命令和恢复逻辑;直接替换为
WebTransport 则会同时引入协议、网关、网络和运维风险。混合渐进式路线能以较小范围验证
收益,并保留可回退路径。
## 本地依据与限制
- 依据当前工作树源码和 `home-cc-web` codebase-memory 索引(索引状态:ready,节点
8138、边 19178)完成;未修改现有源码。
- 评估假设仍是单 Node/PM2 实例、文件会话存储和现有反向代理形态;如果部署已经具备
HTTP/3 网关、共享消息总线或强制 Cookie 会话,WebTransport/SSE 的成本会下降。
- 本次只读核对发现 `ccweb` 的 PM2 进程约 21 分钟前被外部定时单元重启,重启计数为
61,环境中带有 `TRIGGER_UNIT=ccweb-restart-final-1789391174.timer`;该重启不是本次
评估触发的。
## 参考标准
- WHATWG Server-sent events:<https://html.spec.whatwg.org/multipage/server-sent-events.html>
- MDN Server-sent events:<https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events>
- RFC 9297 WebTransport:<https://www.rfc-editor.org/rfc/rfc9297.html>
- RFC 9298 WebTransport over HTTP/3:<https://www.rfc-editor.org/rfc/rfc9298.html>
- MDN WebTransport API:<https://developer.mozilla.org/en-US/docs/Web/API/WebTransport>