第四阶段2全景:从处理器组装到上线返回

块1:L1708-1765 — 插件热替换基础设施。replaceAttachedPluginRuntime(loaded) 运行时更换插件注册表的工具函数,做五件事:

  1. 替换全局 pluginRegistry 引用
  2. 重建 attachedGatewayMethodRegistry(合并新旧方法描述符)
  3. 刷新 runtimeState.gatewayMethods 列表
  4. 调用三个 pin* 函数重新绑定插件路由/会话扩展/通道注册
  5. 刷新节点的插件工具列表

refreshAttachedGatewayDiscovery(nextPluginRegistry) 刷新gataway发现器,让别的服务也能发现gateway的这些方法。插件变了→Bonjour 广播也要变。先停旧的、再启新的。

块2:L1769-1880 — 重新加载gateway插件,插件热重载引擎

reloadAttachedGatewayPlugins({ nextConfig, changedPaths, ... }) 这是 config reload 时用的核心函数,流程:

对比新旧 channel 集合

    │

    ├─ 找出需要停掉的 channel(删除的 / 配置变更的)

    ├─ 调用 beforeReplace() 停掉它们

    ├─ commitRuntime() 提交新 runtime

    ├─ prepareGatewayPluginLoad() 加载新插件

    ├─ replaceAttachedPluginRuntime() 替换注册表

    ├─ 停旧 pluginServices → 启新 pluginServices

    ├─ refreshAttachedGatewayDiscovery() 刷新发现

    └─ 对比前后 channel diff → 返回 restartChannels

关键设计:先停后启,不是原地修改——旧 pluginServices 在替换完成后才 stop,确保过渡期不影响正在处理的请求。

块3:L1886-1980 — 创建 GatewayRequestContext

设置gateway实例的运行时环境、gateway请求上下文(approvalEvents,recoveryRuntime)

const gatewayRequestContext = await startupTrace.measure(

  "gateway.request-context",

  () => createGatewayRequestContext({...})

);

这是整个 gateway 的**"上帝对象"**,几乎所有 handler 都通过它访问公共能力。里面包含了:

类别

内容

运行时配置

getRuntimeConfig, runtimeState, deps

审批系统

execApprovalManager, pluginApprovalManager, systemAgentApprovalManager

健康检查

getHealthCache, refreshHealthSnapshot, getEventLoopHealth

广播/节点

broadcast, broadcastToConnIds, nodeSendToSession, nodeRegistry

终端

terminalSessions, terminalLaunchPolicy

Chat Run

chatAbortControllers, chatRunState(buffers/plan/delta), addChatRun/removeChatRun

会话事件

subscribeSessionEvents, sessionEventSubscribers, sessionMessageSubscribers

Wizard

wizardSessions, findRunningWizard, purgeWizardSession

Worker

workerEnvironmentService, workerSessionPlacementService

块4:L1981-1991 — 创建 GatewayInstanceRuntime

const gatewayInstanceRuntimeLocal = createGatewayInstanceRuntime({

  getContext: () => gatewayRequestContext,

  getMethodRegistry: () => attachedGatewayMethodRegistry,

  isDispatchAvailable: () => gatewayInstanceDispatchReady && !closePreludeStarted,

});

// 回填到 request context

gatewayRequestContext.approvalEvents = gatewayInstanceRuntimeLocal.approvalEvents;

gatewayRequestContext.recoveryRuntime = gatewayInstanceRuntimeLocal.recovery;

gatewayInstanceRuntime 是请求分发的入口,控制着:

  • 哪些方法可以调用(method registry)
  • 当前是否可以分发(dispatch available)
  • 审批事件推送
  • 恢复运行时(restart recovery)

gatewayInstanceDispatchReady 此时还是 false,要等 HTTP 服务器真正 listen 后才翻 true。这是重要的安全阀——bind 之前任何请求都不能进入。

块5:L1993-2023 — 回退上下文 + 延迟插件

setFallbackGatewayContextResolver(() => gatewayRequestContext);

设置一个 fallback,某些极端情况下(比如还没创建完就需要 gateway context)能用。

if (!minimalTestGateway && runtimePluginsLoaded && deferredConfiguredChannelPluginIds.length > 0) {

  reloadDeferredGatewayPlugins(...)  // 加载延迟配置的 channel 插件

}

有些插件不需要在早期启动时就加载,而是在此时延迟加载。

块6:L2025-2075 — WebSocket 处理器挂载 + 开始监听

await startupTrace.measure("gateway.ws-attach", () =>

  attachGatewayWsHandlers({ wss, clients, ... })

);

把 WebSocket 的 upgrade/connection/message 等事件处理器挂载到 wss(WebSocket Server)上。此时客户端还不能连接。

构建wss(WebSocket Server) handler  对象 L2033-2062

await startupTrace.measure("http.listen", () => startListening());

gatewayInstanceDispatchReady = true;

startupTrace.mark("http.bound");

这两行是整个启动流程的分水岭。

  • startListening() → HTTP 服务器在指定端口上开始 listen(),接受 TCP 连接
  • gatewayInstanceDispatchReady = true → 分发阀门打开,请求可以进来了
  • "http.bound" → 启动追踪标记完成

块7:L2073-2219 — 延迟服务激活 + Post-Attach 运行时

const activateScheduledServicesWhenReady = () => {

  // 四个条件全部满足才激活:

  // ① 不在关闭中  ② postAttachRuntime 已返回

  // ③ 所有 sidecar 就绪  ④ 还没激活过

  ...

};

这是一个多条件门控:定时服务(cron、heartbeat 等)只在所有依赖都就绪后才启动。

L2082-2102:

        加载计划服务模块:

                再次检查closePreludeStarted的状态

                激活gateway计划服务

                设置运行时状态(runtimeState)的心跳、停止刷新调用的模型的花费 

startGatewayPostAttachRuntime({ L2109-2219

  // Tailscale 设置 L2122-2127

  // 启动通道 (startChannels) L2165-2167

  // 加载启动插件 L2155-2162

  // Worker 环境运行时 L2191-2206

  // Provider 认证预热 L2214-2216

  onSidecarsReady: () => {  L2207-2210

    startupSidecarsReady = true;

    activateScheduledServicesWhenReady();  // ← 触发门控!

  },

  onChannelsStarted: () => { L2165-2167

    releaseStartupAccountStarts();  // ← 释放之前阻塞的 account 启动

  },

});

回调设计很优雅:所有异步的 sidecar 完成后,各自调用回调,最终 converge 到 onSidecarsReady→激活定时服务。

L2220-2234:

        开启gateway进程的内存使用情况的跟踪日志

        标志:gateway ready

        完成gateway重启内存情况跟踪日志

        如果非测试环境,在runtimeState中加入一条: 验证openclaw数据库完整性

        postAttachRuntimeReturned 为true

        激活准备好的计划服务(activateScheduledServicesWhenReady)

块8:L2237-2390 — Config Reloader + Post-Ready 维护

startManagedGatewayConfigReloader()

  ├─ 监听配置文件变更 L2246-2261

  ├─ 比对 hooks/cron/heartbeat/channel 状态 L2264-2270

  ├─ 热更新 runtimeState(不重启进程)L2271-2282

  ├─ 需要时调用 reloadAttachedGatewayPlugins() L2287

  └─ 支持 cron 重启、终端配置变更等 L2293-2307

promoteConfigSnapshotToLastKnownGood()  L2319-2321

  └─ 把启动时加载的配置标记为"已知良好"备份

scheduleGatewayPostReadyMaintenance()

  ├─ 延迟 POST_READY_MAINTENANCE_DELAY_MS  L2325

  ├─ 启动周期 tick L2348

  ├─ 启动健康检查定时器 L2349

  ├─ 启动 dedupe 清理 L2350

  ├─ 启动 media 清理 L2351

  ├─ 启动 worktree 清理 L2352

  └─ 启动 cron(如果还没启动)L2355

scheduleGatewayIdleTask() L2371-2383

  └─ 清理旧版 plugin 安装残留

块9:L2396-EOF — 返回 + Close 函数

} catch (err) {

  await closeOnStartupFailure();  // 任何阶段失败 → 执行完整关闭流程

  throw err;

}

const close = createCloseHandler();  // 创建关闭处理器

return {

  close: async (optsLocal) => {

    await beginClosePrelude();           // ① 标记 draining

    terminalSessions.disposeAll();       // ② 关所有终端

    await stopRegisteredGatewayLifetimeSidecars();  // ③ 停生命周期 sidecar

    await stopRegisteredPostReadySidecars();        // ④ 停 post-ready sidecar

    await runGlobalGatewayStopSafely(); // ⑤ 执行 gateway_stop 钩子

    await runClosePrelude();            // ⑥ 完整 prelude 清理

    await close(optsLocal);             // ⑦ 最终关闭(HTTP/WS/通道/cron...)

    clearFallbackGatewayContextForServer();  // ⑧ 清回退上下文

  },

};

createGatewayServer 最终只返回一个东西:{ close } 服务器本身是无头的——启动后由内部定时器、WebSocket 连接、事件循环驱动,不需要外部调用任何方法。

完整启动时序总结

createGatewayServer()

├─ Phase 1: 配置解析 (L1-L500+)

├─ Phase 2: 基础设施 (L500-L1117)

├─ Phase 3: 网络栈创建 (L1118-L1232)

│   ├─ TLS 加载

│   ├─ ChannelManager 创建

│   ├─ ReadinessChecker 创建

│   └─ createGatewayRuntimeState() → HTTP/WS 服务器创建(未 listen)

├─ Phase 4: 运行时组装 (L1233-结束) ◀── 我们这次看的全部

│   ├─ 4.1 终端/Lane/Cron/Close工具函数

│   ├─ 4.2 审批系统 + Gateway 方法注册表

│   ├─ 4.3 插件热替换基础设施

│   ├─ 4.4 GatewayRequestContext(上帝对象)

│   ├─ 4.5 GatewayInstanceRuntime(分发入口)

│   ├─ 4.6 WS 处理器挂载 + startListening() ��

│   ├─ 4.7 延迟服务门控 + Post-Attach Runtime

│   ├─ 4.8 Config Reloader + Post-Ready 维护

│   └─ 4.9 return { close }

└─ 运行时:事件循环驱动,等待 WS 连接/HTTP 请求/cron 触发...

到这里 server.impl.ts 就全部读完了。这个文件是 gateway 的心脏,掌握了它的结构就等于掌握了整个 OpenClaw Gateway 的骨架。

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐