Gateway 深度解析

📚 学习路径:本文档是架构文档的第 5 部分。建议按顺序阅读:

前言

Gateway 是 OpenClaw 的核心控制平面,负责协调整个系统的运行。本文档将深入解析 Gateway 的设计、启动流程、消息处理机制以及 24×7 运行能力。

通过阅读本文档,你将能够:

  • 理解 Gateway 的核心作用和设计理念
  • 掌握 Gateway 的启动流程和初始化过程
  • 了解消息接入、路由和分发的完整机制
  • 理解 Gateway 如何实现 24×7 持续运行

一、Gateway 的作用

1.1 为什么需要 Gateway?

如果 AI 助手需要 24×7 运行,那么必然需要一个持久运行的控制平面。这个控制平面需要:

  • 保持与所有消息渠道的长连接
  • 管理会话状态
  • 响应客户端请求
  • 处理定时任务

Gateway 就是这个控制平面。

1.2 Gateway 的核心职责

👥 客户端层

📱 渠道层

🌐 Gateway 控制平面

长连接管理

会话管理

消息路由

定时任务

事件分发

状态持久化

WhatsApp

Telegram

Discord

Web UI

CLI

Mobile

核心职责

职责 说明 实现方式
长连接管理 保持与所有渠道的持久连接 WebSocket + 平台 SDK
会话管理 管理所有会话状态和上下文 Session 存储系统
消息路由 将消息路由到正确的 Agent 路由决策引擎
定时任务 执行定时任务和计划任务 Cron 服务
事件分发 分发系统事件到各组件 事件总线
状态持久化 持久化系统状态到磁盘 文件系统

1.3 Gateway vs 网络网关

重要区别:OpenClaw 的 Gateway 不是网络网关,而是业务网关。

维度 网络网关 OpenClaw Gateway
主要职责 路由、转发、负载均衡 会话管理、状态维护、工具调度
状态管理 无状态 有状态(会话、记忆、配置)
持久化 不持久化数据 持久化会话、记忆、凭据
业务逻辑 无业务逻辑 包含完整的业务编排逻辑
生命周期 随请求结束 7×24 小时持续运行

二、Gateway 启动流程

2.1 启动入口

代码位置src/gateway/server.impl.ts

export async function startGatewayServer(
  port = 18789,
  opts: GatewayServerOptions = {},
): Promise<GatewayServer> {
  // 1. 设置端口环境变量
  process.env.OPENCLAW_GATEWAY_PORT = String(port);

  // 2. 加载并验证配置
  let configSnapshot = await readConfigFileSnapshot();

  // 3. 创建 WebSocket 服务器
  const wsServer = new WebSocket.Server({ port, host });

  // 4. 注册核心处理器
  const channelManager = createChannelManager(configSnapshot.config);
  const agentEventHandler = createAgentEventHandler(configSnapshot.config);
  const cronService = buildGatewayCronService(configSnapshot.config);

  // 5. 启动通道连接(WhatsApp、Telegram 等)
  await channelManager.startAll();

  // 6. 返回 close 方法用于优雅关闭
  return {
    close: (opts) => shutdownGateway(opts),
  };
}

2.2 启动步骤详解

Cron Service Agent Event Handler Channel Manager WebSocket Server 配置加载器 Gateway Server CLI 命令 Cron Service Agent Event Handler Channel Manager WebSocket Server 配置加载器 Gateway Server CLI 命令 startGatewayServer(port) readConfigFileSnapshot() configSnapshot new WebSocket.Server(port) wsServer createChannelManager(config) createAgentEventHandler(config) buildGatewayCronService(config) startAll() 所有渠道已启动 GatewayServer 对象

步骤详解

  1. 端口配置

    • 设置环境变量 OPENCLAW_GATEWAY_PORT
    • 默认端口:18789
    • 可通过配置文件修改
  2. 配置加载

    • 读取配置文件 ~/.openclaw/openclaw.json
    • 验证配置有效性
    • 创建配置快照用于热加载
  3. WebSocket 服务器创建

    • 绑定指定端口
    • 监听客户端连接
    • 处理 WebSocket 消息
  4. 核心组件注册

    • Channel Manager:管理所有渠道连接
    • Agent Event Handler:处理 Agent 事件
    • Cron Service:处理定时任务
  5. 通道启动

    • 启动所有注册的渠道
    • 建立与平台的连接
    • 注册消息处理器
  6. 返回控制接口

    • 提供 close() 方法
    • 支持优雅关闭
    • 清理资源

2.3 配置加载机制

代码位置src/config/config-file.ts

export async function readConfigFileSnapshot(): Promise<ConfigFileSnapshot> {
  // 1. 读取配置文件路径
  const configPath = resolveConfigFilePath();

  // 2. 读取文件内容
  const content = await fs.readFile(configPath, 'utf-8');

  // 3. 解析 JSON5(支持注释和末尾逗号)
  const config = parseJSON5(content);

  // 4. 验证配置
  const validated = validateConfig(config);

  // 5. 返回快照
  return {
    config: validated,
    mtime: (await fs.stat(configPath)).mtime,
  };
}

配置优先级

  1. 环境变量(最高优先级)
  2. 配置文件 ~/.openclaw/openclaw.json
  3. 内置默认值(最低优先级)

三、Gateway 的 24×7 运行机制

3.1 系统服务集成

代码位置src/cli/gateway-cli/run.ts

Gateway 通过系统服务管理保持 24×7 运行:

macOS 启动 Gateway
# 加载服务
launchctl load ~/Library/LaunchAgents/ai.openclaw.gateway.plist

# 启动服务
launchctl start ai.openclaw.gateway

# 停止服务
launchctl stop ai.openclaw.gateway

# 卸载服务
launchctl unload ~/Library/LaunchAgents/ai.openclaw.gateway.plist

LaunchAgent 配置示例

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>ai.openclaw.gateway</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/local/bin/openclaw</string>
        <string>gateway</string>
    </array>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
    <key>StandardOutPath</key>
    <string>/tmp/openclaw-gateway.out.log</string>
    <key>StandardErrorPath</key>
    <string>/tmp/openclaw-gateway.err.log</string>
</dict>
</plist>
Linux 启动 Gateway
# 启用并启动服务
systemctl --user enable --now openclaw-gateway.service

# 查看状态
systemctl --user status openclaw-gateway.service

# 停止服务
systemctl --user stop openclaw-gateway.service

# 查看日志
journalctl --user -u openclaw-gateway.service -f

Systemd 服务配置示例

[Unit]
Description=OpenClaw Gateway
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/openclaw gateway
Restart=always
RestartSec=5

[Install]
WantedBy=default.target

3.2 核心特性

实现方式

24×7 运行特性

持久运行

长连接维护

优雅关闭

健康检查

自动重启

故障恢复

系统服务管理

WebSocket 心跳

信号处理

健康检查接口

KeepAlive 配置

错误重试机制

特性 说明 实现方式
持久运行 作为系统服务启动,自动重启 LaunchAgent / Systemd
长连接维护 保持与所有渠道的 WebSocket 连接 心跳检测 + 自动重连
优雅关闭 支持优雅关闭,避免数据丢失 信号处理(SIGTERM)
健康检查 提供健康检查接口,监控运行状态 HTTP 健康检查端点
自动重启 进程崩溃后自动重启 KeepAlive 配置
故障恢复 单个组件失败不影响整体 错误隔离 + 重试机制

3.3 优雅关闭机制

代码位置src/gateway/server.impl.ts

export async function shutdownGateway(opts: GatewayShutdownOptions = {}): Promise<void> {
  const { timeout = 30000 } = opts;

  // 1. 停止接收新消息
  await channelManager.stopAcceptingNewMessages();

  // 2. 等待当前消息处理完成
  await Promise.race([
    waitForActiveMessagesToComplete(),
    timeoutMs(timeout),
  ]);

  // 3. 停止所有渠道连接
  await channelManager.stopAll();

  // 4. 关闭 WebSocket 服务器
  await wsServer.close();

  // 5. 清理资源
  await cleanupResources();

  log.info('Gateway shutdown complete');
}

关闭流程

  1. 停止接收新消息

    • 标记为"正在关闭"
    • 拒绝新的 WebSocket 连接
    • 停止接收渠道消息
  2. 等待当前消息处理完成

    • 等待所有活跃的 Agent 完成
    • 超时后强制终止(默认 30 秒)
  3. 停止所有渠道连接

    • 关闭 WebSocket 连接
    • 断开平台 SDK 连接
    • 清理渠道状态
  4. 关闭 WebSocket 服务器

    • 停止监听端口
    • 关闭所有客户端连接
    • 释放端口资源
  5. 清理资源

    • 关闭文件句柄
    • 清理定时任务
    • 释放内存

四、消息接入与分发

4.1 消息接入流程

Agent 路由器 Gateway 渠道适配器 聊天平台 用户 Agent 路由器 Gateway 渠道适配器 聊天平台 用户 发送消息 推送消息 身份验证 访问控制 消息解析 InboundEnvelope 重新加载配置 resolveAgentRoute() 路由决策 生成 Session Key RouteDecision 分发消息

4.2 Telegram 消息处理示例

代码位置src/channels/plugins/runtime/runtime-telegram.ts

Telegram 使用 grammY 作为机器人框架,注册监听事件,回复普通消息。

import { Bot, webhookCallback } from "grammy";

const bot = new Bot(opts.token, client ? { client } : undefined);
const processMessage = createTelegramMessageProcessor({bot, ...});
registerTelegramHandlers({ cfg, accountId ,bot, processMessage, ...});

4.3 核心分发函数

代码位置src/channels/plugins/

createTelegramMessageProcessor 里使用核心分发函数 dispatchTelegramMessage 负责处理来自 Telegram 的消息,以及注册回复消息的回调函数 deliverReplies

export const dispatchTelegramMessage = async ({
  context,
  bot,
  cfg,
  runtime,
  // ... 更多参数
}) => {
  const { msg, chatId, isGroup, historyKey, route } = context;

  // 1. 分发消息
  const { queuedFinal } = await dispatchReplyWithBufferedBlockDispatcher({
    ctx: ctxPayload,
    cfg,
    dispatcherOptions: {
      // 2. 回复消息回调
      deliver: async (payload, info) => {
        const result = await deliverReplies({
          replies: [payload],
          chatId: String(chatId),
          token: opts.token,
          runtime,
          bot,
          // ... 更多选项
        });
      },
      onError: (err, info) => {
        runtime.error?.(`telegram reply failed: ${String(err)}`);
      },
    },
  });
};

消息分发流程

  1. 接收消息:从渠道接收原始消息
  2. 消息解析:提取消息内容、发送者信息、会话标识
  3. 访问控制:检查发送者权限、会话类型
  4. 消息分发:将消息路由到对应的 Agent
  5. 回复回调:注册回复消息的回调函数

4.4 消息标准化

代码位置src/plugin-sdk/inbound-envelope.ts

export function createInboundEnvelope(
  rawMessage: PlatformMessage,
  options: EnvelopeOptions,
): InboundEnvelope {
  return {
    id: generateId(),
    platform: options.platform,
    content: extractContent(rawMessage),
    attachments: extractAttachments(rawMessage),
    sender: normalizeSender(rawMessage.from),
    timestamp: rawMessage.timestamp,
    replyTo: rawMessage.replyTo,
    // ... 更多标准化字段
  };
}

标准化内容

原始数据 标准化字段 说明
消息 ID id 生成唯一 ID
平台类型 platform whatsapp/telegram/discord 等
消息内容 content 统一的内容格式
附件 attachments 图片、文件等
发送者 sender 统一的发送者信息
时间戳 timestamp ISO 8601 格式
回复上下文 replyTo 回复的消息 ID

五、路由决策机制

5.1 路由决策流程

代码位置src/routing/resolve-route.ts

export function resolveAgentRoute(
  envelope: InboundEnvelope,
  config: OpenClawConfig,
): RouteDecision {
  // 1. 生成 Session Key
  const sessionKey = buildSessionKey(envelope);

  // 2. 匹配 Agent 绑定
  const agentId = matchAgentBinding(envelope, config.bindings);

  // 3. 检查权限
  const permissions = checkPermissions(envelope, config.accessControl);

  // 4. 回声检测
  if (isEcho(envelope)) {
    return { skip: true, reason: 'echo' };
  }

  return {
    sessionKey,
    agentId,
    permissions,
    // ... 更多路由信息
  };
}

5.2 Session Key 生成

代码位置src/routing/session-key.ts

export function buildAgentMainSessionKey(params: {
  agentId: string;
  mainKey?: string | undefined;
}): string {
  const agentId = normalizeAgentId(params.agentId);
  const mainKey = normalizeMainKey(params.mainKey);
  return `agent:${agentId}:${mainKey}`;
}

export function buildAgentPeerSessionKey(params: {
  agentId: string;
  mainKey?: string | undefined;
  channel: string;
  accountId?: string | null;
  peerKind?: "dm" | "group" | "channel" | null;
  peerId?: string | null;
  // ...
}): string {
  // 对于 DM: agent:main:channel:account:dm:peerId
  // 对于群组: agent:main:channel:group:groupId
  // ...
}

Session Key 格式示例

类型 Session Key 说明
主会话 agent:main:main 用户自己的主会话
Telegram DM agent:main:telegram:default:dm:123456789 Telegram 私聊
Telegram 群组 agent:main:telegram:group:100123456789 Telegram 群聊
WhatsApp DM agent:main:whatsapp:default:dm:+8613800138000 WhatsApp 私聊
Discord 群组 agent:main:discord:group:123456789012345678 Discord 群聊

5.3 访问控制

代码位置src/channels/allowlist-match.ts

export function checkPermissions(
  envelope: InboundEnvelope,
  accessControl: AccessControlConfig,
): PermissionResult {
  // 1. 检查白名单
  if (!isInAllowlist(envelope.sender.id, accessControl.allowlist)) {
    return { allowed: false, reason: 'not_in_allowlist' };
  }

  // 2. 检查私聊策略
  if (envelope.isDM && accessControl.dmPolicy === 'pairing') {
    if (!isPaired(envelope.sender.id)) {
      return { allowed: false, reason: 'not_paired' };
    }
  }

  // 3. 检查群聊策略
  if (envelope.isGroup && accessControl.groupPolicy === 'mention_only') {
    if (!isMentioned(envelope)) {
      return { allowed: false, reason: 'not_mentioned' };
    }
  }

  return { allowed: true };
}

访问控制策略

策略 选项 说明
白名单 允许列表 只允许白名单中的用户
私聊策略 pairing / open / disabled 配对模式 / 开放 / 禁止
群聊策略 mention_only / always / disabled 仅 @ 提及 / 总是回复 / 禁止

六、事件系统

6.1 事件总线

代码位置src/infra/agent-events.ts

export type AgentEventPayload = {
  runId: string;
  seq: number;
  stream: AgentEventStream;
  ts: number;
  data: Record<string, unknown>;
  sessionKey?: string;
};

export function emitAgentEvent(event: Omit<AgentEventPayload, "seq" | "ts">) {
  const nextSeq = (seqByRun.get(event.runId) ?? 0) + 1;
  seqByRun.set(event.runId, nextSeq);
  const context = runContextById.get(event.runId);
  const enriched: AgentEventPayload = {
    ...event,
    sessionKey: context?.sessionKey,
    seq: nextSeq,
    ts: Date.now(),
  };
  for (const listener of listeners) {
    try {
      listener(enriched);
    } catch {
      /* 忽略单个监听器错误 */
    }
  }
}

6.2 事件流类型

export type AgentEventStream =
  | "lifecycle"    // 生命周期事件
  | "tool"         // 工具调用事件
  | "assistant"    // 助手回复事件
  | "error"        // 错误事件
  | (string & {}); // 自定义事件流

6.3 事件订阅

export function onAgentEvent(
  listener: (evt: AgentEventPayload) => void
): () => void {
  listeners.add(listener);
  return () => listeners.delete(listener); // 返回取消订阅函数
}

使用示例

// 订阅事件
const unsubscribe = onAgentEvent((event) => {
  if (event.stream === 'tool') {
    console.log('Tool called:', event.data);
  }
});

// 取消订阅
unsubscribe();

七、定时任务系统

7.1 Cron 服务

代码位置src/gateway/cron-service.ts

export function buildGatewayCronService(
  config: OpenClawConfig,
): CronService {
  const service = new CronService();

  // 注册定时任务
  if (config.cronJobs) {
    for (const job of config.cronJobs) {
      service.schedule(job.cronExpression, async () => {
        await executeCronJob(job);
      });
    }
  }

  return service;
}

7.2 定时任务配置

配置示例

{
  "cronJobs": [
    {
      "name": "daily_summary",
      "cronExpression": "0 9 * * *",
      "agentId": "main",
      "message": "生成今日工作总结"
    },
    {
      "name": "hourly_check",
      "cronExpression": "0 * * * *",
      "agentId": "monitor",
      "message": "检查系统状态"
    }
  ]
}

八、核心代码文件索引

文件路径 功能 重要性
src/gateway/server.impl.ts Gateway 服务器主实现 ⭐⭐⭐⭐⭐
src/gateway/server.ts Gateway 服务器入口 ⭐⭐⭐⭐⭐
src/cli/gateway-cli/run.ts CLI 启动命令 ⭐⭐⭐⭐
src/config/config-file.ts 配置文件加载 ⭐⭐⭐⭐
src/routing/resolve-route.ts 路由决策 ⭐⭐⭐⭐⭐
src/routing/session-key.ts Session Key 生成 ⭐⭐⭐⭐
src/infra/agent-events.ts 事件系统 ⭐⭐⭐⭐
src/gateway/cron-service.ts 定时任务服务 ⭐⭐⭐
src/channels/registry.ts 渠道注册表 ⭐⭐⭐⭐
src/channels/allowlist-match.ts 访问控制 ⭐⭐⭐

九、下一步

恭喜你完成了 Gateway 深度解析的学习!接下来建议:


通过理解 Gateway 的工作原理,你已经掌握了 OpenClaw 的控制平面设计!

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐