跳转至

Middleware

1. 模块定位

1.1 核心作用

Middleware 是横切关注点处理器,提供一种可插拔的方式来处理消息的通用逻辑,如日志记录、认证授权、消息转换等。

一句话定义:Middleware 是消息处理的"拦截器",在不修改核心逻辑的情况下添加通用功能。

1.2 设计目标

目标 说明
可插拔 中间件可独立添加、移除、替换
链式处理 多个中间件按顺序组成处理链
透明性 对业务逻辑透明,不侵入核心代码
可扩展 用户可自定义中间件

1.3 职责边界

graph TB
    subgraph "Middleware 职责范围"
        subgraph "✅ 适合处理"
            A1[日志记录]
            A2[认证授权]
            A3[请求追踪]
            A4[消息验证]
            A5[性能监控]
        end
        subgraph "❌ 不适合处理"
            B1[业务逻辑]
            B2[协议解析]
            B3[消息路由]
            B4[数据持久化]
        end
    end

2. 设计决策

2.1 为什么使用中间件模式?

问题背景: - 多个横切关注点需要处理 - 这些关注点与业务逻辑无关 - 需要灵活组合和配置

没有中间件的情况

graph LR
    subgraph "业务代码中混杂"
        A[日志代码]
        B[认证代码]
        C[业务逻辑]
        D[监控代码]
    end

    A --> B --> C --> D

    style A fill:#f99
    style B fill:#f99
    style D fill:#f99

问题:业务代码被横切逻辑污染,难以维护

有中间件的情况

graph LR
    subgraph "中间件链"
        M1[Logging]
        M2[Auth]
        M3[Metrics]
    end

    subgraph "核心"
        C[业务逻辑]
    end

    M1 --> M2 --> M3 --> C

    style C fill:#9f9

优势:横切逻辑与业务逻辑分离,可独立维护

2.2 为什么使用责任链模式?

决策:中间件采用责任链模式,每个中间件决定是否继续执行下一个。

sequenceDiagram
    participant M1 as Middleware 1
    participant M2 as Middleware 2
    participant M3 as Middleware 3
    participant H as Handler

    M1->>M1: 前置处理
    M1->>M2: next()
    M2->>M2: 前置处理
    M2->>M3: next()
    M3->>M3: 前置处理
    M3->>H: 执行
    H-->>M3: 返回
    M3->>M3: 后置处理
    M3-->>M2: 返回
    M2->>M2: 后置处理
    M2-->>M1: 返回
    M1->>M1: 后置处理

理由: 1. 灵活控制:中间件可决定是否继续执行 2. 前后处理:可在调用前后添加逻辑 3. 错误传播:错误可沿链向上传播

2.3 为什么中间件不处理业务错误?

决策:中间件只处理横切逻辑,不干预业务逻辑的错误处理。

graph TB
    subgraph "中间件层"
        M[Middleware]
    end

    subgraph "业务层"
        H[Handler]
    end

    M --> H

    H -- "业务错误" --> M
    M -- "记录日志,继续传播" --> GW[Gateway]

    style M fill:#9f9

理由: 1. 职责分离:中间件不应理解业务语义 2. 一致性:所有错误统一由 Gateway 处理 3. 简单性:避免复杂的错误分类逻辑


3. 接口契约

3.1 Middleware 接口

// Middleware 中间件接口
type Middleware interface {
    // Name 返回中间件名称,用于日志和调试
    Name() string

    // Handle 处理消息
    // ctx: 上下文
    // msg: 当前消息
    // next: 调用下一个中间件或最终处理器
    Handle(ctx context.Context, msg *Message, next Handler) error
}

// Handler 处理函数类型
type Handler func(ctx context.Context, msg *Message) error

3.2 MiddlewareChain 接口

// MiddlewareChain 中间件链
type MiddlewareChain interface {
    // Use 添加中间件到链末尾
    Use(middleware Middleware)

    // Execute 执行中间件链
    Execute(ctx context.Context, msg *Message) error
}

3.3 接口设计说明

方法 调用时机 行为约定
Name() 日志输出时 返回可读的中间件名称
Handle() 消息处理时 可在 next 前后添加逻辑,或直接返回错误
Use() 初始化时 添加中间件,按添加顺序执行
Execute() 消息到达时 按顺序执行所有中间件

4. 执行流程设计

4.1 中间件链执行流程

sequenceDiagram
    participant GW as Gateway
    participant MC as MiddlewareChain
    participant M1 as Logging
    participant M2 as Auth
    participant H as Final Handler

    GW->>MC: Execute(ctx, msg)
    MC->>M1: Handle(ctx, msg, next)

    Note over M1: 前置:记录请求

    M1->>M2: next(ctx, msg)

    Note over M2: 前置:验证 Token

    alt Token 有效
        M2->>H: next(ctx, msg)
        H-->>M2: nil
        Note over M2: 后置:无操作
        M2-->>M1: nil
        Note over M1: 后置:记录响应
        M1-->>MC: nil
        MC-->>GW: nil
    else Token 无效
        M2-->>M1: ErrUnauthorized
        Note over M1: 后置:记录错误
        M1-->>MC: ErrUnauthorized
        MC-->>GW: ErrUnauthorized
    end

4.2 中间件跳过后续处理

sequenceDiagram
    participant MC as MiddlewareChain
    participant M1 as RateLimit
    participant M2 as Auth
    participant H as Handler

    MC->>M1: Handle(ctx, msg, next)

    alt 限流触发
        M1-->>MC: ErrRateLimited
        Note over M2,H: 不执行后续中间件
    else 正常
        M1->>M2: next(ctx, msg)
        M2->>H: next(ctx, msg)
        H-->>M2: nil
        M2-->>M1: nil
        M1-->>MC: nil
    end

5. 内置中间件设计

5.1 日志中间件

作用:记录消息处理的请求和响应日志。

sequenceDiagram
    participant M as LoggingMiddleware
    participant N as Next

    M->>M: 记录请求开始时间
    M->>M: 记录请求信息

    M->>N: next(ctx, msg)
    N-->>M: error

    M->>M: 计算处理耗时
    M->>M: 记录响应信息

    M-->>M: 返回 error

日志格式

level=INFO msg="message processed" channel=wechat duration=15ms error=nil

5.2 认证中间件

作用:验证消息中的 Token 是否有效。

sequenceDiagram
    participant M as AuthMiddleware
    participant N as Next

    M->>M: 从 Metadata 提取 Token

    alt Token 存在且有效
        M->>N: next(ctx, msg)
        N-->>M: error
        M-->>M: 返回 error
    else Token 无效
        M-->>M: 返回 ErrUnauthorized
    end

Token 来源: - 入站消息:从 Webhook 参数或 Header 提取 - 出站消息:从 WebSocket 消息 Metadata 提取

5.3 追踪中间件

作用:为消息添加追踪 ID,便于日志关联。

sequenceDiagram
    participant M as TraceMiddleware
    participant N as Next

    M->>M: 检查 TraceID

    alt TraceID 不存在
        M->>M: 生成 TraceID
        M->>M: 添加到 msg.Metadata
    end

    M->>M: 添加到 ctx

    M->>N: next(ctx, msg)
    N-->>M: error
    M-->>M: 返回 error

6. 与其他模块的关系

6.1 依赖关系

graph TB
    subgraph "中间件"
        LM[LoggingMiddleware]
        AM[AuthMiddleware]
        TM[TraceMiddleware]
    end

    GW[Gateway] --> MC[MiddlewareChain]
    MC --> LM
    MC --> AM
    MC --> TM

    style MC fill:#9f9

6.2 交互方式

交互方 交互方式 说明
Gateway 注册中间件 通过 Use 方法添加
Gateway 执行中间件链 调用 Execute 方法
Message 读写数据 中间件可读取/修改消息

7. 约束与限制

7.1 使用约束

约束 原因
中间件必须调用 next 否则链会中断
中间件不应阻塞 影响整体性能
中间件顺序敏感 不同顺序可能产生不同结果

7.2 性能考虑

考虑点 建议
中间件数量 不超过 10 个
单个中间件耗时 不超过 1ms
避免重复计算 使用缓存

7.3 错误处理约定

错误类型 处理方式
认证失败 返回 ErrUnauthorized
限流触发 返回 ErrRateLimited
内部错误 返回原始错误

8. 扩展指南

8.1 自定义中间件步骤

flowchart TB
    A[1. 实现 Middleware 接口] --> B[2. 实现 Name 方法]
    B --> C[3. 实现 Handle 方法]
    C --> D[4. 在 Handle 中调用 next]
    D --> E[5. 通过 Gateway.Use 注册]

8.2 实现检查清单

检查项 说明
☐ 实现 Name() 返回名称
☐ 实现 Handle() 方法
☐ 正确调用 next 必须调用
☐ 处理 next 返回的错误
☐ 不修改消息的只读字段
☐ 编写单元测试

8.3 示例:自定义限流中间件

type RateLimitMiddleware struct {
    limiter *rate.Limiter
}

func (m *RateLimitMiddleware) Name() string {
    return "RateLimit"
}

func (m *RateLimitMiddleware) Handle(ctx context.Context, msg *Message, next Handler) error {
    if !m.limiter.Allow() {
        return ErrRateLimited
    }
    return next(ctx, msg)
}

9. 使用场景

9.1 开发环境配置

graph LR
    subgraph "开发环境"
        M1[Logging]
        M2[Trace]
    end

    M1 --> M2

9.2 生产环境配置

graph LR
    subgraph "生产环境"
        M1[Trace]
        M2[Auth]
        M3[Logging]
        M4[Metrics]
    end

    M1 --> M2 --> M3 --> M4

10. 相关文档