4.4 KiB
4.4 KiB
Design: postmessage-duplex 通信层重构
Context
当前系统使用原生 postMessage API 实现 SW 与应用层通信:
- 应用层 → SW:直接
controller.postMessage() - SW → 应用层:
broadcastToClients()广播所有窗口
这种模式在多标签页场景下存在问题:
- 多个页面同时提交相同任务,SW 的去重检查存在时间窗口
- 广播消息所有页面都会收到,无法区分请求来源
- 请求-响应模式需要手动用 RxJS 实现
Goals / Non-Goals
Goals
- 使用 postmessage-duplex 实现双工通信
- 解决多标签页任务重复问题
- 简化请求-响应模式的实现
- 保持向后兼容(渐进式迁移)
Non-Goals
- 不改变现有业务逻辑
- 不改变 IndexedDB 存储结构
- 不改变任务执行流程
Decisions
1. 通信架构
┌─────────────────┐ ┌──────────────────────┐ ┌─────────────────┐
│ Tab 1 (Page) │ │ Service Worker │ │ Tab 2 (Page) │
├─────────────────┤ ├──────────────────────┤ ├─────────────────┤
│ ServiceWorker │◄──────►│ Per-client channels │◄──────►│ ServiceWorker │
│ Channel │ RPC │ Map<clientId,Channel>│ RPC │ Channel │
└─────────────────┘ └──────────────────────┘ └─────────────────┘
- 每个页面创建独立的
ServiceWorkerChannel - SW 端维护
Map<clientId, channel>管理所有连接 - 请求-响应使用
call方法,事件通知使用subscribe
2. 消息类型划分
| 操作 | 当前方式 | 改造后方式 |
|---|---|---|
| 任务创建 | postMessage + 监听 | call('task:create', params) → {taskId, status} |
| 任务取消 | postMessage | call('task:cancel', {taskId}) → {success} |
| 任务重试 | postMessage | call('task:retry', {taskId}) → {success} |
| 获取任务列表 | postMessage + timeout | call('task:list') → {tasks} |
| 任务状态变更 | broadcast | subscribe('task:status') |
| 任务完成 | broadcast | subscribe('task:completed') |
| Chat 流式 | broadcast | subscribe('chat:chunk') |
3. 任务创建流程改造
当前流程:
1. 应用层生成 taskId
2. 本地创建任务(乐观更新)
3. postMessage 到 SW
4. SW 检查重复,可能拒绝
5. 广播 TASK_CREATED
改造后流程:
1. 应用层调用 channel.call('task:create', params)
2. SW 检查重复
- 已存在:返回 {success: false, existingTaskId}
- 新建:创建任务,返回 {success: true, task}
3. 应用层根据响应更新本地状态
4. SW 广播 task:created 给其他客户端(排除发起者)
4. 多页面同步
- SW 维护所有通道引用
- 当任务状态变更时,广播给所有订阅该事件的通道
- 每个通道独立订阅,避免重复处理
5. Service Worker 端集成
由于 SW 不支持直接 import npm 模块,有两种方案:
方案 A(推荐):打包时内联
- 使用 Vite 的
?inline或自定义打包将库代码内联到 SW
方案 B:复制核心代码
- 将 postmessage-duplex 的 SW 相关代码复制到项目中
选择 方案 A,在 SW 构建配置中处理。
Risks / Trade-offs
| Risk | Mitigation |
|---|---|
| SW 端集成复杂 | 使用打包工具处理,文档中有示例 |
| 迁移风险 | 渐进式迁移,保留旧消息类型兼容 |
| 库稳定性 | 库较新(v1.0.0),需关注 issues |
Migration Plan
Phase 1:基础设施
- 安装 postmessage-duplex 依赖
- 配置 SW 打包支持
- 创建新的通信服务(与现有并存)
Phase 2:核心功能迁移
- 迁移任务创建为 call 模式
- 迁移任务状态为 subscribe 模式
- 迁移 Chat 流式为 subscribe 模式
Phase 3:清理
- 移除旧的 RxJS 订阅逻辑
- 移除兼容代码
- 更新文档
Open Questions
-
SW 打包方式的具体实现细节?
- 需要测试 Vite 对 SW 入口的打包行为
-
是否需要支持消息压缩?
- 当前任务数据量不大,暂不需要