OpenClaw 学习系列之七:Gateway 深度解析
Gateway 是 OpenClaw 的核心控制平面,负责协调整个系统的运行。本文档将深入解析 Gateway 的设计、启动流程、消息处理机制以及 24×7 运行能力。理解 Gateway 的核心作用和设计理念掌握 Gateway 的启动流程和初始化过程了解消息接入、路由和分发的完整机制理解 Gateway 如何实现 24×7 持续运行。
Gateway 深度解析
📚 学习路径:本文档是架构文档的第 5 部分。建议按顺序阅读:
前言
Gateway 是 OpenClaw 的核心控制平面,负责协调整个系统的运行。本文档将深入解析 Gateway 的设计、启动流程、消息处理机制以及 24×7 运行能力。
通过阅读本文档,你将能够:
- 理解 Gateway 的核心作用和设计理念
- 掌握 Gateway 的启动流程和初始化过程
- 了解消息接入、路由和分发的完整机制
- 理解 Gateway 如何实现 24×7 持续运行
一、Gateway 的作用
1.1 为什么需要 Gateway?
如果 AI 助手需要 24×7 运行,那么必然需要一个持久运行的控制平面。这个控制平面需要:
- ✅ 保持与所有消息渠道的长连接
- ✅ 管理会话状态
- ✅ 响应客户端请求
- ✅ 处理定时任务
Gateway 就是这个控制平面。
1.2 Gateway 的核心职责
核心职责:
| 职责 | 说明 | 实现方式 |
|---|---|---|
| 长连接管理 | 保持与所有渠道的持久连接 | 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 启动步骤详解
步骤详解:
-
端口配置
- 设置环境变量
OPENCLAW_GATEWAY_PORT - 默认端口:18789
- 可通过配置文件修改
- 设置环境变量
-
配置加载
- 读取配置文件
~/.openclaw/openclaw.json - 验证配置有效性
- 创建配置快照用于热加载
- 读取配置文件
-
WebSocket 服务器创建
- 绑定指定端口
- 监听客户端连接
- 处理 WebSocket 消息
-
核心组件注册
- Channel Manager:管理所有渠道连接
- Agent Event Handler:处理 Agent 事件
- Cron Service:处理定时任务
-
通道启动
- 启动所有注册的渠道
- 建立与平台的连接
- 注册消息处理器
-
返回控制接口
- 提供
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,
};
}
配置优先级:
- 环境变量(最高优先级)
- 配置文件
~/.openclaw/openclaw.json - 内置默认值(最低优先级)
三、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 核心特性
| 特性 | 说明 | 实现方式 |
|---|---|---|
| 持久运行 | 作为系统服务启动,自动重启 | 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');
}
关闭流程:
-
停止接收新消息
- 标记为"正在关闭"
- 拒绝新的 WebSocket 连接
- 停止接收渠道消息
-
等待当前消息处理完成
- 等待所有活跃的 Agent 完成
- 超时后强制终止(默认 30 秒)
-
停止所有渠道连接
- 关闭 WebSocket 连接
- 断开平台 SDK 连接
- 清理渠道状态
-
关闭 WebSocket 服务器
- 停止监听端口
- 关闭所有客户端连接
- 释放端口资源
-
清理资源
- 关闭文件句柄
- 清理定时任务
- 释放内存
四、消息接入与分发
4.1 消息接入流程
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 核心分发函数
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)}`);
},
},
});
};
消息分发流程:
- 接收消息:从渠道接收原始消息
- 消息解析:提取消息内容、发送者信息、会话标识
- 访问控制:检查发送者权限、会话类型
- 消息分发:将消息路由到对应的 Agent
- 回复回调:注册回复消息的回调函数
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 深度解析的学习!接下来建议:
- 📖 阅读 06. Agent 运行机制 - 了解 Agent 如何工作
- 🔄 查看 03. 消息流转 - 理解完整消息流程
- 🛠️ 探索 message_flow/ - 查看详细步骤文档
通过理解 Gateway 的工作原理,你已经掌握了 OpenClaw 的控制平面设计!
更多推荐

所有评论(0)