12 KiB
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 认证,然后按 JSONtype分发命令。- 客户端在
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 网关或大规模压测;多实例部署会增加工作量。
推荐实施路线
- 先把
wsSend、sendSessionEventToViewers、全局广播和运行时sendRuntime收敛到 一个传输无关的事件发布接口,统一事件名、sessionId、requestId、时间戳和递增eventId。保留现有 WebSocket 适配器,确保这一步行为不变。 - 增加受保护的
/api/eventsSSE 端点,只接入只读会话列表、任务事件和运行时下行事件。 对每个连接限制队列,发送注释心跳,并支持Last-Event-ID后按会话重发快照。 - 以 Cookie 或
fetch流读取解决认证,不把长期 token 放入 URL;命令端点使用requestId和幂等键,返回 202 后由 SSE 回传结果。先迁移message、abort、会话 切换和审批响应,再迁移设置与任务看板命令。 - 对比 WebSocket 与 SSE 的首字延迟、完整回合延迟、断线恢复丢事件数、慢客户端内存、 代理超时和移动网络表现。通过特性开关按用户或会话灰度,WebSocket 保留为回退。
- 只有在所有命令均有 HTTP 等价物、事件重放和多实例路由验证通过后,才考虑下线默认 WebSocket;机器人和内部集成需单独迁移,不能只看浏览器 UI。
- 如确有 QUIC 需求,再建立独立 WebTransport PoC,先验证目标浏览器、TLS/HTTP3 网关、 UDP 网络、可靠流重连和监控,再决定是否替代 SSE/WS。
最终建议
对 cc-web 当前“模型流式输出 + 交互式审批 + 会话控制 + 单 Node 进程状态绑定”的 形态,优先级应为:
- 近期:继续 WebSocket,先做传输无关事件层和协议整理。
- 中期:SSE 下行 + HTTP 命令灰度,逐步减少对 WebSocket 的依赖。
- 长期:只有在确认 HTTP/3/QUIC 基础设施和用户浏览器覆盖后,才评估 WebTransport。
直接替换为 SSE 会把双向协议问题转移到大量 HTTP 命令和恢复逻辑;直接替换为 WebTransport 则会同时引入协议、网关、网络和运维风险。混合渐进式路线能以较小范围验证 收益,并保留可回退路径。
本地依据与限制
- 依据当前工作树源码和
home-cc-webcodebase-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