vault backup: 2026-06-03 10:22:41
This commit is contained in:
@@ -0,0 +1,305 @@
|
||||
# 10 — 中间件链
|
||||
|
||||
> **一句话概括**:Logger → Recovery → Metrics → Auth → RateLimit → Handler,洋葱模型,层层守护请求处理。
|
||||
|
||||
---
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["🌐 Request"] --> B["📝 Logger"]
|
||||
B --> C["🛡️ Recovery"]
|
||||
C --> D["📊 Metrics"]
|
||||
D --> E["🔐 Auth"]
|
||||
E --> F["🚦 RateLimit"]
|
||||
F --> G["⚙️ Handler"]
|
||||
G --> F
|
||||
F --> E
|
||||
E --> D
|
||||
D --> C
|
||||
C --> B
|
||||
B --> H["📡 Response"]
|
||||
|
||||
style A fill:#e3f2fd,stroke:#1976d2
|
||||
style B fill:#e8f5e9,stroke:#388e3c
|
||||
style C fill:#fff3e0,stroke:#f57c00
|
||||
style D fill:#fce4ec,stroke:#c62828
|
||||
style E fill:#f3e5f5,stroke:#7b1fa2
|
||||
style F fill:#e0f7fa,stroke:#00838f
|
||||
style G fill:#fff8e1,stroke:#f9a825
|
||||
style H fill:#e8eaf6,stroke:#303f9f
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧅 洋葱模型
|
||||
|
||||
Gin 的中间件采用**洋葱模型**:请求从外到内穿过各中间件,响应从内到外返回。每个中间件可以在 `c.Next()` 前后执行逻辑。
|
||||
|
||||
```
|
||||
请求 → Logger.enter → Recovery.enter → Metrics.enter → Auth.enter → RateLimit.enter → Handler
|
||||
响应 ← Logger.leave ← Recovery.leave ← Metrics.leave ← Auth.leave ← RateLimit.leave ← Handler
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 Logger — 请求日志
|
||||
|
||||
**职责**:为每个请求生成唯一 ID,记录请求详情。
|
||||
|
||||
### 核心逻辑
|
||||
|
||||
```go
|
||||
func Logger() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
requestID := generateRequestID() // 时间戳 + 8位随机hex
|
||||
c.Set("request_id", requestID)
|
||||
c.Header("X-Request-ID", requestID)
|
||||
c.Next()
|
||||
// 根据状态码选择日志级别
|
||||
if status >= 500 → Error
|
||||
if status >= 400 → Warn
|
||||
else → Info
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### RequestID 生成
|
||||
|
||||
```go
|
||||
func generateRequestID() string {
|
||||
b := make([]byte, 8)
|
||||
rand.Read(b)
|
||||
return fmt.Sprintf("%d-%x", time.Now().UnixMilli(), b)
|
||||
}
|
||||
```
|
||||
|
||||
格式:`1717286400000-a1b2c3d4e5f67890`
|
||||
|
||||
| 组件 | 作用 |
|
||||
|------|------|
|
||||
| 时间戳(毫秒) | 保证时间有序性 |
|
||||
| 8 字节随机 hex | 保证唯一性 |
|
||||
|
||||
### 日志级别映射
|
||||
|
||||
| HTTP 状态码 | 日志级别 | 含义 |
|
||||
|:-----------:|:-------:|------|
|
||||
| 5xx | ERROR | 服务器错误 |
|
||||
| 4xx | WARN | 客户端错误 |
|
||||
| 2xx/3xx | INFO | 正常请求 |
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ Recovery — Panic 恢复
|
||||
|
||||
**职责**:捕获未处理的 panic,防止服务崩溃。
|
||||
|
||||
```go
|
||||
func Recovery() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
// 记录完整堆栈
|
||||
l.Error("panic recovered",
|
||||
"error", fmt.Sprintf("%v", r),
|
||||
"stack", string(debug.Stack()),
|
||||
)
|
||||
// 返回结构化 500
|
||||
c.AbortWithStatusJSON(500, gin.H{
|
||||
"code": 500,
|
||||
"message": "服务器内部错误",
|
||||
})
|
||||
}
|
||||
}()
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**关键特性**:
|
||||
- 捕获所有未处理的 panic
|
||||
- 记录完整的 request 上下文和堆栈信息
|
||||
- 返回统一格式的 500 错误,避免泄露内部信息
|
||||
- 使用 `defer` 保证即使 panic 也能执行清理逻辑
|
||||
|
||||
---
|
||||
|
||||
## 📊 Metrics — 指标采集
|
||||
|
||||
**职责**:自动采集 HTTP 请求的性能指标。
|
||||
|
||||
```go
|
||||
func Metrics() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
path := c.FullPath() // 路由模板,避免高基数
|
||||
HTTPRequestsInFlight.Inc()
|
||||
defer HTTPRequestsInFlight.Dec()
|
||||
|
||||
reqSize := c.Request.ContentLength // 请求体大小(before)
|
||||
c.Next()
|
||||
elapsed := time.Since(start) // 耗时(after)
|
||||
HTTPRequestsTotal.WithLabelValues(...).Inc()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**采集时机**:
|
||||
|
||||
| 指标 | 采集时机 | 原因 |
|
||||
|------|---------|------|
|
||||
| InFlight | 进入时 +1,离开时 -1 | 使用 defer 保证异常时也能递减 |
|
||||
| RequestSize | `c.Next()` 之前 | Content-Length 此时已知 |
|
||||
| ResponseSize | `c.Next()` 之后 | Writer 此时已写入 |
|
||||
| Duration | `c.Next()` 之后 | 需要计算总耗时 |
|
||||
|
||||
> 💡 **FullPath() 的关键作用**:使用路由模板 `/api/v1/tasks/:taskId` 而非实际路径,避免高基数标签导致 Prometheus 内存爆炸。
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Auth — JWT 认证
|
||||
|
||||
**职责**:验证 Bearer token,提取用户身份。
|
||||
|
||||
```go
|
||||
func AuthMiddleware(jwtSecret string) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// 1. 提取 Authorization header
|
||||
authHeader := c.GetHeader("Authorization")
|
||||
tokenString := strings.TrimPrefix(authHeader, "Bearer ")
|
||||
|
||||
// 2. 解析和验证 JWT
|
||||
token, _ := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, []byte) {
|
||||
return []byte(jwtSecret), nil
|
||||
})
|
||||
|
||||
// 3. 提取 sub claim → userID
|
||||
claims := token.Claims.(jwt.MapClaims)
|
||||
c.Set("userID", claims["sub"])
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**作用范围**:仅应用于 `v1Auth` 路由组,公开接口(如 `/health`)不经过认证。
|
||||
|
||||
**错误处理**:
|
||||
|
||||
| 场景 | HTTP 状态码 | 消息 |
|
||||
|------|:-----------:|------|
|
||||
| 无 token | 401 | 未提供认证令牌 |
|
||||
| 格式错误 | 401 | 认证格式错误,需为 Bearer \<token\> |
|
||||
| token 无效 | 401 | 令牌无效或已过期 |
|
||||
| 解析失败 | 401 | 令牌解析失败 |
|
||||
|
||||
---
|
||||
|
||||
## 🚦 RateLimit — 限流
|
||||
|
||||
**职责**:按路由配置执行双层限流(全局 + 用户)。
|
||||
|
||||
```go
|
||||
// 每个路由独立配置
|
||||
v1Auth.POST("/generate",
|
||||
ratelimit.RateLimit(globalLimiter, ..., "global", "generate"),
|
||||
ratelimit.RateLimit(userLimiter, ..., "user", "generate"),
|
||||
handler.Generate,
|
||||
)
|
||||
```
|
||||
|
||||
详见 [09 — 限流](09-rate-limiting.md)。
|
||||
|
||||
---
|
||||
|
||||
## 🔗 中间件挂载
|
||||
|
||||
### 全局链(所有请求)
|
||||
|
||||
```go
|
||||
r := gin.New()
|
||||
r.Use(mildware.Logger()) // 1. 请求日志
|
||||
r.Use(mildware.Recovery()) // 2. Panic 恢复
|
||||
r.Use(mildware.Metrics()) // 3. 指标采集
|
||||
```
|
||||
|
||||
### 路由组级链(需认证)
|
||||
|
||||
```go
|
||||
v1Auth := r.Group("/api/v1")
|
||||
v1Auth.Use(mildware.AuthMiddleware(cfg.JWT.Secret)) // 4. JWT 认证
|
||||
```
|
||||
|
||||
### 端点级链(需限流)
|
||||
|
||||
```go
|
||||
v1Auth.POST("/generate",
|
||||
ratelimit.RateLimit(globalLimiter, ...), // 5. 全局限流
|
||||
ratelimit.RateLimit(userLimiter, ...), // 6. 用户限流
|
||||
handler.Generate, // 7. 业务处理
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 端点链示例:/api/v1/generate
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
A["POST /api/v1/generate"] --> B["Logger<br/>生成 request_id"]
|
||||
B --> C["Recovery<br/>注册 panic 恢复"]
|
||||
C --> D["Metrics<br/>InFlight++, 记录 reqSize"]
|
||||
D --> E["Auth<br/>验证 JWT, 提取 userID"]
|
||||
E --> F["RateLimit Global<br/>检查全局限额"]
|
||||
F --> G["RateLimit User<br/>检查用户限额"]
|
||||
G --> H["Handler.Generate<br/>执行生成逻辑"]
|
||||
H --> I["Metrics<br/>记录 duration, respSize"]
|
||||
I --> J["Recovery<br/>检查是否 panic"]
|
||||
J --> K["Logger<br/>记录请求日志"]
|
||||
K --> L["📡 Response"]
|
||||
|
||||
style A fill:#e3f2fd,stroke:#1976d2
|
||||
style B fill:#e8f5e9,stroke:#388e3c
|
||||
style C fill:#fff3e0,stroke:#f57c00
|
||||
style D fill:#fce4ec,stroke:#c62828
|
||||
style E fill:#f3e5f5,stroke:#7b1fa2
|
||||
style F fill:#e0f7fa,stroke:#00838f
|
||||
style G fill:#e0f7fa,stroke:#00838f
|
||||
style H fill:#fff8e1,stroke:#f9a825
|
||||
style L fill:#e8eaf6,stroke:#303f9f
|
||||
```
|
||||
|
||||
### 完整请求生命周期
|
||||
|
||||
| 阶段 | 中间件 | 动作 |
|
||||
|:----:|--------|------|
|
||||
| 1 | Logger | 生成 request_id,注入 context 和 header |
|
||||
| 2 | Recovery | 注册 defer panic 恢复 |
|
||||
| 3 | Metrics | InFlight +1,记录请求体大小 |
|
||||
| 4 | Auth | 验证 JWT,提取 userID |
|
||||
| 5 | RateLimit | 检查全局限额 |
|
||||
| 6 | RateLimit | 检查用户限额 |
|
||||
| 7 | Handler | 执行业务逻辑 |
|
||||
| 8 | Metrics | 记录耗时、响应体大小,InFlight -1 |
|
||||
| 9 | Recovery | 检查是否发生 panic |
|
||||
| 10 | Logger | 记录请求日志(含状态码、耗时) |
|
||||
|
||||
---
|
||||
|
||||
## 📊 中间件职责矩阵
|
||||
|
||||
| 中间件 | 请求进入 | 请求离开 | 异常处理 | 作用范围 |
|
||||
|--------|---------|---------|---------|---------|
|
||||
| Logger | 生成 request_id | 记录日志 | — | 全局 |
|
||||
| Recovery | 注册 defer | — | panic → 500 | 全局 |
|
||||
| Metrics | InFlight++, reqSize | duration, respSize | — | 全局 |
|
||||
| Auth | 验证 JWT | — | 无效 → 401 | v1Auth |
|
||||
| RateLimit | 检查配额 | — | 超限 → 429 | 端点级 |
|
||||
|
||||
---
|
||||
|
||||
## 🔗 关联文档
|
||||
|
||||
- [← 返回索引](00-index.md)
|
||||
- [07 — 可观测性](07-observability.md) — Metrics 中间件采集的指标
|
||||
- [09 — 限流](09-rate-limiting.md) — RateLimit 中间件的详细实现
|
||||
- [08 — SSE 实时推送](08-sse-push.md) — SSE 端点的中间件配置
|
||||
Reference in New Issue
Block a user