Skip to content

Day 1 — Gateway 设计文档

设计目标:构建统一的消息协议和路由基础设施,让系统内部的所有消息都有标准格式、能按类型分发、支持旁路事件通知。


一、核心接口设计

1.1 GatewayMessage

字段类型必填说明
msg_idstr自动UUID v4,全局唯一标识
msg_typeMessageTypeTEXT / COMMAND / EVENT / ERROR
sourcestr消息来源标识
targetstr消息目标标识
payloadAny业务数据
timestampdatetime自动UTC 时间戳

为什么这样设计:

  • msg_id 用 UUID 而非自增:分布式场景下无需中心化 ID 生成器
  • msg_type 用 StrEnum:JSON 序列化零成本,MessageType.TEXT 直接就是 "text"
  • timestamp 统一 UTC:避免多时区部署时的混乱
  • 继承 Pydantic BaseModel:自动获得格式校验、序列化、文档能力

1.2 EventBus

方法签名说明
onon(event: str, handler: Callable)注册事件监听器
offoff(event: str, handler: Callable)移除事件监听器
emitasync emit(event: str, **kwargs)触发事件,并发执行所有监听器

1.3 MessageRouter

方法签名说明
registerregister(msg_type: MessageType, handler)注册类型处理器
set_defaultset_default(handler)设置兜底处理器
routeasync route(message: GatewayMessage)路由消息到对应处理器

1.4 GatewayServer

方法签名说明
startasync start()启动 WebSocket 服务
stopasync stop()优雅关闭
broadcastasync broadcast(message)广播给所有连接

二、关键流程图

消息处理流程


三、设计决策与权衡

决策 1:使用 Pydantic 而非 dataclass

方案优点缺点
Pydantic(选择)自动校验、序列化、IDE 友好多一个依赖
dataclass标准库,无依赖无校验,序列化要手写
TypedDict轻量无运行时校验,IDE 支持弱

选择理由:Pydantic 是 AI/Web 领域事实上的标准(FastAPI、LangChain 都用),且后续 LLM 交互也需要结构化数据,统一用 Pydantic 减少心智负担。

决策 2:EventBus 失败隔离

当一个事件有多个监听器时,某个监听器抛出异常不影响其他监听器:

python
await asyncio.gather(*tasks, return_exceptions=True)

权衡:如果用 return_exceptions=False,一个失败会取消其他所有监听器——在"通知类"场景中这太激进了。但缺点是错误会被静默吞掉,需要配合日志来发现问题。

决策 3:连接管理用 set 而非 list

方案添加删除查找
set(选择)O(1)O(1)O(1)
listO(1)O(n)O(n)

WebSocket 连接频繁建立和断开,set 的 O(1) 删除性能更优。


四、与前序章节的集成点

这是第一个模块,没有前序依赖。它为后续所有章节提供:

  • GatewayMessage:系统内统一的消息格式
  • EventBus:组件间松耦合通知
  • MessageRouter:按消息类型分发处理

五、与真实生产系统的对比

维度miniOpenClaw生产级实现
协议自定义 GatewayMessageProtocol Buffers / gRPC
事件总线进程内 asyncioRedis Pub/Sub / Kafka
连接管理单机内存 set分布式连接管理(Redis + Nginx)
序列化JSON(可读性好)Protobuf / MessagePack(性能高)
监控loggingOpenTelemetry + Prometheus