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
日志格式:
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. 相关文档¶
- 01-gateway.md - Gateway 模块设计
- 05-message.md - Message 数据结构