跳转至

Gateway

1. 模块定位

1.1 核心作用

Gateway 是 gort 的核心协调器,作为整个系统的中枢,负责协调各组件协同工作。

一句话定义:Gateway 是消息的"交通指挥官",负责将消息从来源正确地路由到目的地。

1.2 设计目标

目标 说明
统一入口 所有外部消息通过 Gateway 进入系统
协调分发 协调 Channel、SessionManager、业务层之间的消息流转
生命周期管理 管理所有组件的启动和停止顺序
解耦组件 各组件通过 Gateway 交互,不直接依赖

1.3 职责边界

graph TB
    subgraph "Gateway 职责范围"
        subgraph "✅ 负责"
            A1[接收 Channel 转发的消息]
            A2[接收客户端消息]
            A3[管理 Channel 注册和生命周期]
            A4[管理 HTTP/WebSocket 服务器]
            A5[协调中间件链执行]
        end
        subgraph "❌ 不负责"
            B1[具体协议解析 - Channel]
            B2[WebSocket 连接维护 - SessionManager]
            B3[业务逻辑处理 - 业务层]
            B4[消息持久化存储]
        end
    end

2. 设计决策

2.1 为什么需要 Gateway?

问题背景: - 多个 Channel(微信、钉钉、飞书)需要统一接入 - 多个 WebSocket 客户端需要接收消息 - 消息需要在 Channel 和客户端之间双向流转

没有 Gateway 的情况

graph LR
    WC[微信 Channel] <--> DC[钉钉 Channel]
    DC <--> FC[飞书 Channel]
    WC <--> WS[WebSocket 客户端]
    DC <--> WS
    FC <--> WS

    style WC fill:#f99
    style DC fill:#f99
    style FC fill:#f99
    style WS fill:#f99

问题:组件之间直接依赖,耦合严重,难以扩展

有 Gateway 的情况

graph TB
    subgraph "外部渠道"
        WC[微信 Channel]
        DC[钉钉 Channel]
        FC[飞书 Channel]
    end

    subgraph "核心"
        GW[Gateway]
    end

    subgraph "客户端"
        WS[WebSocket 客户端]
    end

    subgraph "业务"
        BH[业务层 Handler]
    end

    WC --> GW
    DC --> GW
    FC --> GW
    GW <--> WS
    GW --> BH

    style GW fill:#9f9

优势:所有组件通过 Gateway 交互,职责清晰,易于扩展

2.2 为什么使用统一 HTTP 服务器?

决策:所有 Channel 共享一个 HTTP 服务器,通过路径区分不同渠道。

方案对比

graph LR
    subgraph "方案A: 独立端口"
        A1[微信 :8080]
        A2[钉钉 :8081]
        A3[飞书 :8082]
    end

    subgraph "方案B: 统一服务器 ✅"
        B1[HTTP :8080]
        B2["/webhook/wechat"]
        B3["/webhook/dingtalk"]
        B4["/webhook/feishu"]
        B1 --> B2
        B1 --> B3
        B1 --> B4
    end

    style B1 fill:#9f9
方案 优点 缺点
独立端口 隔离性好 防火墙配置复杂,端口管理困难
统一服务器 ✅ 只需开放一个端口,配置简单 需要路径路由

选择统一服务器的原因: 1. 桌面 App 场景下,简化网络配置更重要 2. 路径路由是成熟模式,实现简单 3. 便于统一日志、监控、安全控制

2.3 为什么使用回调模式注入 Handler?

决策:Channel 启动时通过参数注入消息处理器,而非后续设置。

sequenceDiagram
    participant GW as Gateway
    participant CH as Channel

    Note over GW,CH: ✅ 采用的方式
    GW->>CH: Start(ctx, handler)
    Note right of CH: handler 是必需参数
不会遗漏 Note over GW,CH: ❌ 不采用的方式 GW->>CH: Start(ctx) Note right of CH: 可能忘记设置 handler GW--xCH: SetOnMessage(handler)? Note right of CH: 设置时机不确定

理由: 1. 避免状态不一致:启动时必须提供 handler,不会遗漏 2. 明确依赖关系:handler 是 Channel 运行的必要条件 3. 简化生命周期:不需要管理 handler 的设置时机


3. 接口契约

3.1 核心接口

// Gateway 网关接口
type Gateway interface {
    // 生命周期
    Start(ctx context.Context) error
    Stop(ctx context.Context) error
    IsRunning() bool

    // Channel 管理
    RegisterChannel(channel Channel) error
    GetChannel(name string) (Channel, bool)

    // 消息处理
    HandleChannelMessage(ctx context.Context, msg *Message) error
    HandleClientMessage(ctx context.Context, clientID string, msg *Message) error

    // 业务处理器注册
    RegisterClientHandler(handler ClientHandler)
    RegisterChannelHandler(handler ChannelHandler)

    // 中间件
    Use(middleware Middleware)

    // 状态查询
    GetClientCount() int
}

3.2 接口设计说明

方法 调用时机 行为约定
RegisterChannel Start 之前 注册 Channel,重复注册返回错误
Start 初始化完成后 启动所有组件,失败时自动回滚
Stop 收到关闭信号 等待消息处理完成,超时强制关闭
HandleChannelMessage Channel 收到消息 执行中间件 → 业务处理 → 推送客户端
HandleClientMessage 客户端发送消息 执行中间件 → 业务处理 → 发送到 Channel

3.3 配置项

配置项 类型 默认值 说明
Name string "gort-gateway" 网关名称,用于日志标识
HTTPAddr string ":8080" HTTP 服务监听地址
WebSocketAddr string ":9000" WebSocket 服务监听地址
ReadTimeout duration 30s 读取超时
WriteTimeout duration 30s 写入超时

4. 消息流转设计

4.1 入站消息流程

来源:外部 IM 平台(微信、钉钉等) 目的地:WebSocket 客户端

sequenceDiagram
    participant EP as 外部平台
    participant WH as Webhook
    participant CH as Channel
    participant GW as Gateway
    participant MW as 中间件链
    participant BH as ChannelHandler
    participant SM as SessionManager
    participant WS as WebSocket客户端

    EP->>WH: HTTP POST
    WH->>CH: 触发 Webhook
    CH->>CH: 协议解析
    CH->>GW: HandleChannelMessage(msg)
    GW->>MW: 执行中间件链
    MW-->>GW: 继续
    GW->>BH: 业务处理
    BH-->>GW: 返回响应消息
    GW->>SM: Broadcast(response)
    SM->>WS: WebSocket 推送

设计要点: - 中间件按注册顺序执行 - 业务 Handler 可返回 nil 表示无需响应 - 广播失败记录日志,不中断流程

4.2 出站消息流程

来源:WebSocket 客户端 目的地:外部 IM 平台

sequenceDiagram
    participant WS as WebSocket客户端
    participant SM as SessionManager
    participant GW as Gateway
    participant MW as 中间件链
    participant BH as ClientHandler
    participant CH as Channel
    participant EP as 外部平台

    WS->>SM: 发送消息
    SM->>GW: HandleClientMessage(clientID, msg)
    GW->>MW: 执行中间件链
    MW-->>GW: 继续
    GW->>BH: 业务处理
    BH-->>GW: 返回响应消息
    GW->>CH: SendMessage(msg)
    CH->>CH: 转换为平台格式
    CH->>EP: HTTP POST/API 调用
    EP-->>CH: 响应

设计要点: - 消息必须指定目标 ChannelID - Channel 不存在时返回错误 - 发送失败由 Channel 内部重试


5. 与其他模块的关系

5.1 依赖关系

graph TB
    GW[Gateway]

    GW --> CH[Channel
协议适配] GW --> SM[SessionManager
连接管理] GW --> MW[Middleware
横切处理] BL[业务层] --> GW style GW fill:#9f9

5.2 交互方式

交互方 交互方式 说明
Channel 回调注入 Channel 启动时注入消息处理器
SessionManager 直接持有 Gateway 创建并管理 SessionManager
Middleware 链式调用 按注册顺序执行中间件
业务层 Handler 注册 业务层注册处理函数

6. 约束与限制

6.1 使用约束

约束 原因
RegisterChannel 必须在 Start 之前调用 启动时需要知道所有 Channel
Stop 后不能重新 Start 简化生命周期管理
消息处理是异步的 避免阻塞调用方

6.2 性能限制

限制 说明
单实例运行 不支持分布式部署
内存级转发 消息不持久化
无内置限流 依赖外部平台限制

6.3 错误处理约定

错误类型 处理方式
启动失败 返回错误,已启动组件自动回滚
消息处理失败 记录日志,不影响其他消息
Channel 发送失败 由 Channel 内部重试

7. 扩展点

7.1 可扩展的设计

扩展点 扩展方式
新增渠道 实现 Channel 接口,注册到 Gateway
新增中间件 实现 Middleware 接口,通过 Use 添加
自定义业务逻辑 注册 ClientHandler / ChannelHandler

7.2 不支持扩展的设计

限制 原因
不支持运行时添加 Channel 简化生命周期管理
不支持动态修改中间件链 避免并发问题

8. 使用场景

8.1 典型场景:AI 助手

sequenceDiagram
    participant U as 用户
    participant WX as 微信
    participant WC as WeChatChannel
    participant GW as Gateway
    participant AI as AI业务处理
    participant BR as 浏览器客户端

    U->>WX: 发送消息
    WX->>WC: Webhook
    WC->>GW: 标准消息
    GW->>AI: 处理
    AI-->>GW: AI回复
    GW->>BR: 推送显示
    GW->>WC: 发送回复
    WC->>WX: API调用
    WX->>U: 收到回复

8.2 典型场景:多端同步

sequenceDiagram
    participant DA as 桌面App
    participant GW as Gateway
    participant DC as DingTalkChannel
    participant DT as 钉钉群
    participant BR as 浏览器客户端

    DA->>GW: 发送消息
    GW->>DC: 转发
    DC->>DT: 发送到群
    GW->>BR: 同步推送

9. 相关文档