Skip to content

Service Worker 生命周期管理 ​

本文引用的源码文件

本文引用的文件

目录 ​

  1. 引言
  2. 项目结构
  3. 核心组件
  4. 架构总览
  5. 详细组件分析
  6. 依赖关系分析
  7. 性能与内存优化
  8. 故障排查指南
  9. 结论

引言 ​

本文面向 Manifest V3 扩展的 Service Worker(SW)生命周期管理,围绕本仓库中“保活端口连接策略、SW 重启检测与自动恢复、checkpoint 持久化与重试、终止竞态防护”等主题展开。该扩展采用双进程架构:Chrome 扩展端(MV3,Service Worker 入口为 src/background/index.js)与独立本地代理端 Agent(Node.js)。文档聚焦扩展端 SW 的生命周期机制,不深入 Agent 内部实现。

项目结构 ​

从 MV3 清单和背景脚本入口可以看出,扩展使用 Service Worker 作为后台进程,并通过侧边栏面板发起长任务;SW 通过 keepalive 端口保持活跃,同时借助闹钟实现跨 SW 回收的自动续跑。

graph TB
A["manifest.json<br/>声明 service_worker"] --> B["src/background/index.js<br/>SW 入口与消息路由"]
B --> C["src/background/state.js<br/>keepalive 端口/身份注册表"]
B --> D["src/background/auto-resume.js<br/>自动续跑闹钟与决策"]
E["src/side_panel/chat-manager.js<br/>侧边栏调用与 keepalive 端口"] --> B
F["test/unit/sw-heartbeat.unit.test.js<br/>心跳保活回归测试"] --> B

图表来源

章节来源

核心组件 ​

  • Service Worker 入口与消息路由:负责 keepalive 端口接入、SW 重启检测、面板可见性通知、消息分发、checkpoint 查询与恢复。
  • Keepalive 状态注册表:维护按 sessionId 的 keepalive 端口映射、实例身份(窗口与宿主 tab)、派生会话祖先链解析。
  • 自动续跑模块:基于 chrome.alarms 创建周期闹钟,扫描 checkpoint 并决定是否自动续跑,限制最大尝试次数。
  • 侧边栏调用流程:建立 keepalive 端口、上报实例身份、监听 SW_RESTARTED 通知、统一清理资源。
  • 心跳保活测试:验证任务级保活心跳在长时间空闲间隙仍有效。

章节来源

架构总览 ​

下图展示 MV3 Service Worker 生命周期关键交互:侧边栏发起 API 时建立 keepalive 端口,SW 接收连接并记录身份;若 SW 被系统回收,闹钟唤醒后扫描 checkpoint 自动续跑;SW 重启后通过端口向侧边栏发送 SW_RESTARTED,前端拒绝 Promise 并提示用户继续执行。

sequenceDiagram
participant Panel as "侧边栏"
participant SW as "Service Worker"
participant State as "状态注册表"
participant Alarm as "闹钟调度"
participant DB as "Checkpoint 存储"
Panel->>SW : "connect('keepalive-<sessionId>')"
SW->>State : "registerKeepalivePort(sessionId, port)"
Panel->>SW : "KEEPALIVE_IDENTITY(windowId)"
SW->>State : "setKeepaliveIdentity(sessionId, identity)"
Note over SW : "API 调用期间保持 SW 活跃"
alt "SW 被系统回收"
Alarm->>SW : "onAlarm(AUTO_RESUME_ALARM_NAME)"
SW->>DB : "getAllReactCheckpoints()"
SW->>SW : "decideAutoResume(checkpoints, activeSessions)"
loop "可续跑目标"
SW->>DB : "saveReactCheckpoint(nextAttempts)"
SW->>SW : "resumeReactLoopFromCheckpoint(sessionId)"
end
end
alt "SW 静默重启"
Panel->>SW : "重连 keepalive-<sessionId>"
SW->>Panel : "SW_RESTARTED(checkpointMeta)"
Panel->>Panel : "reject(callApi promise)"
end

图表来源

详细组件分析 ​

Keepalive 端口注册与注销 ​

  • 注册:侧边栏在发起 API 调用时通过 chrome.runtime.connect({ name: 'keepalive-' + mySessionId }) 建立长连接;SW 在 chrome.runtime.onConnect 中识别以 keepalive- 开头的端口名,提取 sessionId 并调用 registerKeepalivePort 注册到状态表。
  • 身份上报:侧边栏通过端口发送 KEEPALIVE_IDENTITY,携带 windowId;SW 解析权威活跃 tab 镜像后回传 HOST_TAB_RESOLVED,覆盖侧边栏乐观值。
  • 注销:端口断开时调用 unregisterKeepalivePort,清理端口映射与身份信息;注销逻辑带端口校验,防止旧连接迟到误删新连接。
flowchart TD
Start(["侧边栏发起 API"]) --> Connect["建立 keepalive 端口"]
Connect --> Register["SW 注册端口到状态表"]
Register --> Identity["上报实例身份 windowId"]
Identity --> Resolve["SW 解析宿主 tab 并回传"]
Resolve --> Active["API 调用期间保持 SW 活跃"]
Active --> Disconnect{"端口断开?"}
Disconnect --> |是| Unregister["注销端口与身份"]
Unregister --> End(["结束"])
Disconnect --> |否| Active

图表来源

章节来源

SW 重启检测与自动恢复 ​

  • 重启检测:当侧边栏重连 keepalive 端口且状态表中已有该 sessionId,SW 判定为重连;若当前无对应 ReAct 循环,则读取 checkpoint 并发送 SW_RESTARTED 通知。
  • 前端处理:侧边栏收到 SW_RESTARTED 后清理定时器与监听器,reject 当前 callApi Promise,并携带 checkpoint 元数据供 UI 展示“继续执行”。
  • 自动恢复:SW 启动时注册自动续跑闹钟回调;闹钟触发后扫描所有 checkpoint,过滤终态与会话已运行项,对可续跑项递增 autoResumeAttempts 并调用 resumeReactLoopFromCheckpoint。
sequenceDiagram
participant Panel as "侧边栏"
participant SW as "Service Worker"
participant State as "状态表"
participant DB as "Checkpoint 存储"
Panel->>SW : "重连 keepalive-<sessionId>"
SW->>State : "hasKeepalivePort(sessionId)"
alt "检测到重连且无活动循环"
SW->>DB : "getReactCheckpoint(sessionId)"
SW->>Panel : "SW_RESTARTED(checkpointMeta)"
Panel->>Panel : "reject(callApi)"
else "首次连接或仍有活动"
SW->>Panel : "正常消息路由"
end

图表来源

章节来源

Checkpoint 持久化与重试策略 ​

  • 持久化:ReAct 循环每轮推理后保存 checkpoint 到 IndexedDB,包含 sessionId、iteration、interruptedReason、currentMessages、subtaskPlan 等字段。
  • 自动续跑决策:decideAutoResume 函数过滤终态原因(user_cancelled/error)、跳过已在 activeReactLoops 中的会话,限制最大尝试次数(默认 3),并对刚耗尽的会话仅通知一次。
  • 重试执行:对每个目标 checkpoint,先回写递增后的 autoResumeAttempts,再调用 resumeReactLoopFromCheckpoint;失败时记录警告日志但不中断其他会话续跑。
flowchart TD
Start(["闹钟触发"]) --> Load["加载所有 checkpoint"]
Load --> Filter["过滤终态与会话已运行"]
Filter --> Decide{"是否超过最大尝试次数?"}
Decide --> |是| Notify["通知人工介入仅一次"]
Decide --> |否| Update["递增 autoResumeAttempts"]
Update --> Resume["resumeReactLoopFromCheckpoint"]
Resume --> Next{"下一个 checkpoint?"}
Next --> |是| Load
Next --> |否| End(["结束"])

图表来源

章节来源

终止竞态防护方案 ​

  • 可选链保护:所有顶层 chrome API 访问均使用可选链(如 chrome.runtime?.onConnect?.addListener?.),避免扩展重载瞬间 API 被剥离导致 TypeError。
  • 状态同步:keepalive 端口注册与注销使用原子操作,注销时校验当前端口是否与传入端口一致,防止旧连接迟到误删新连接。
  • 降级处理:storage 读取失败、tabs.query 不可用等场景均静默降级,不影响核心功能;面板可见性判定保守按“不可见”处理,宁可多弹不漏弹。
flowchart TD
Start(["SW 启动"]) --> Optional["chrome API 可选链访问"]
Optional --> Register["注册 keepalive 端口"]
Register --> Sync["状态同步:端口与身份"]
Sync --> Graceful{"API 可用?"}
Graceful --> |是| Normal["正常流程"]
Graceful --> |否| Fallback["静默降级"]
Fallback --> End(["安全退出"])
Normal --> End

图表来源

章节来源

依赖关系分析 ​

  • 模块耦合:index.js 依赖 state.js 进行 keepalive 状态管理,依赖 auto-resume.js 进行自动续跑,依赖 notifier.js 和 panel-visibility.js 进行通知与可见性判定。
  • 外部依赖:chrome.runtime、chrome.alarms、chrome.storage、chrome.sidePanel 等浏览器 API;IndexedDB 用于 checkpoint 持久化。
  • 潜在循环依赖:auto-resume.js 仅依赖 shared/logger,避免反向依赖业务逻辑;index.js 通过回调注入方式解耦自动续跑执行逻辑。
graph LR
Index["background/index.js"] --> State["background/state.js"]
Index --> AutoResume["background/auto-resume.js"]
Index --> Notifier["background/notifier.js"]
Index --> Visibility["background/panel-visibility.js"]
Panel["side_panel/chat-manager.js"] --> Index
Test["sw-heartbeat.unit.test.js"] --> Index

图表来源

章节来源

性能与内存优化 ​

  • 内存泄漏防护:
    • keepalive 端口在 onDisconnect 时及时注销,避免 Map 无限增长。
    • 流式输出状态在 cleanupCallApi 中清理,包括元素引用、累积内容、定时器与监听器。
    • AbortController 按会话隔离,取消时立即释放网络请求。
  • 资源清理最佳实践:
    • 统一清理函数 cleanupCallApi 集中处理端口断开、定时器清除、状态删除。
    • 工具级 AbortController 单独管理,不干扰 ReAct 循环取消。
  • 性能优化策略:
    • MCP 工具查询缓存避免重复网络请求。
    • 自动续跑闹钟周期设为 0.5 分钟(30s),平衡响应速度与系统开销。
    • 心跳保活通过轻量 API 调用重置闲置计时器,避免频繁保活开销。

章节来源

故障排查指南 ​

  • SW 未启动或 No SW 错误:检查顶层 chrome API 是否被可选链保护,确认扩展重载瞬间未抛出 TypeError。
  • keepalive 端口未生效:确认侧边栏是否正确建立 keepalive-<sessionId> 端口,SW 是否成功注册到状态表。
  • SW 重启后任务丢失:检查 checkpoint 是否存在,自动续跑是否因终态原因被跳过,最大尝试次数是否已耗尽。
  • 面板通知误判:确认实例身份上报是否成功,权威活跃 tab 镜像是否更新,可见性判定是否保守处理缺失值。
  • 内存泄漏:检查 keepalive 端口是否及时注销,流式输出状态是否清理,AbortController 是否释放。

章节来源

结论 ​

本仓库通过 keepalive 端口、闹钟自动续跑、checkpoint 持久化与可选链保护,构建了健壮的 MV3 Service Worker 生命周期管理机制。侧边栏与 SW 之间通过端口通信保持活跃,SW 被系统回收后可自动恢复任务,同时提供用户友好的错误提示与继续执行选项。整体设计注重内存安全、资源清理与性能优化,适合长时间运行的扩展任务场景。