Files
cs-note/hzh/Gen2D/12-三级降级策略.md
T

194 lines
5.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
tags: [fallback-pattern, resilience, degradation, error-handling, go]
create time: 2026-06-03 10:55
---
# 12. 三级降级策略
## 概述
Generate handler 实现三级降级 — TaskQueue -> WorkerPool -> Legacy FIFO,宁可降级也不拒绝服务。
## 正文
### 降级链总览
```mermaid
flowchart TD
R["HTTP Request\nPOST /api/v1/generate"] --> A{"TaskQueue 可用?"}
A -->|"Submit 成功"| S1["200 OK\ntaskId 返回"]
A -->|"Submit 失败"| B{"WorkerPool 可用?"}
B -->|"Submit 成功"| S2["200 OK\ntaskId 返回"]
B -->|"ErrPoolFull"| E2["503 Service\nUnavailable"]
B -->|"ErrUserLimit"| E3["429 Too Many Requests"]
B -->|"Pool 不可用"| C{"Legacy FIFO Queue 可用?"}
C -->|"Enqueue 成功"| S3["200 OK\ntaskId 返回"]
C -->|"Queue 不可用"| E4["500 Internal Server Error"]
style A fill:#4caf50,stroke:#333,color:#fff
style B fill:#ff9800,stroke:#333,color:#fff
style C fill:#f44336,stroke:#333,color:#fff
style S1 fill:#8bc34a,stroke:#333,color:#fff
style S2 fill:#8bc34a,stroke:#333,color:#fff
style S3 fill:#8bc34a,stroke:#333,color:#fff
```
### 三级详解
#### 第一级:TaskQueue(优先路径)
| 属性 | 说明 |
|------|------|
| **组件** | `taskqueue.TaskQueue` 接口(Memory / RabbitMQ) |
| **特点** | 支持持久化、分布式、消息确认 |
| **提交** | `taskQueue.Submit(ctx, msg)` |
| **失败时** | 返回 HTTP 500,标记任务为 `failed` |
```go
// handler/generate.go — 第一级
if taskQueue != nil {
msg := taskqueue.TaskMessage{...}
if err := taskQueue.Submit(ctx, msg); err != nil {
c.JSON(500, "提交任务失败")
return
}
c.JSON(200, taskId)
return // 成功,不再降级
}
```
#### 第二级:WorkerPool(有界并发)
| 属性 | 说明 |
|------|------|
| **组件** | `workerpool.Pool` |
| **特点** | 有界队列 + per-user 并发限制 |
| **提交** | `workerPool.Submit(task)` |
| **失败类型** | `ErrPoolFull` -> 503 / `ErrUserLimitReached` -> 429 |
```go
// handler/generate.go — 第二级
if workerPool != nil {
err := workerPool.Submit(workerpool.Task{...})
switch {
case errors.Is(err, workerpool.ErrPoolFull):
c.JSON(503, "系统繁忙,请稍后重试")
case errors.Is(err, workerpool.ErrUserLimitReached):
c.JSON(429, "您的生成任务已达上限")
default:
c.JSON(500, "提交任务失败")
}
return
}
```
#### 第三级:Legacy FIFO(最后保底)
| 属性 | 说明 |
|------|------|
| **组件** | `service.TaskQueue`(串行 FIFO 队列) |
| **特点** | 零配置、串行执行、无并发控制 |
| **用途** | 兜底,确保系统在任何配置下都能运行 |
```go
// handler/generate.go — 第三级
if generateQueue != nil {
generateQueue.Enqueue(&service.TaskJob{...})
}
c.JSON(200, taskId)
```
---
### HTTP 状态码映射
```mermaid
flowchart LR
subgraph "降级路径"
TQ["TaskQueue"]
WP["WorkerPool"]
LF["Legacy FIFO"]
end
subgraph "HTTP 响应"
E200["200 OK\n任务已接受"]
E429["429 Too Many Requests\n用户限流"]
E500["500 Internal Server Error\n系统错误"]
E503["503 Service Unavailable\n系统繁忙"]
end
TQ -->|"成功"| E200
TQ -->|"失败"| E500
WP -->|"成功"| E200
WP -->|"队列满"| E503
WP -->|"用户限流"| E429
LF -->|"成功"| E200
LF -->|"不可用"| E500
```
| 状态码 | 含义 | 触发条件 |
|--------|------|----------|
| `200` | 任务已接受 | 任意一级提交成功 |
| `429` | 用户限流 | WorkerPool 用户并发达到上限 |
| `500` | 系统内部错误 | TaskQueue 提交失败 / 所有级别不可用 |
| `503` | 服务暂不可用 | WorkerPool 队列已满 |
---
### 为什么需要三级?
```mermaid
flowchart TB
subgraph "第一级:分布式能力"
TQ["TaskQueue\n持久化 + 消息确认\n支持 RabbitMQ 横向扩展"]
end
subgraph "第二级:并发控制"
WP["WorkerPool\n有界队列背压\nper-user 限流保护"]
end
subgraph "第三级:可用性兜底"
LF["Legacy FIFO\n零依赖、零配置\n确保始终可运行"]
end
TQ -->|"不可用时降级到"| WP
WP -->|"不可用时降级到"| LF
```
| 级别 | 核心价值 | 典型场景 |
|------|----------|----------|
| TaskQueue | 分布式 + 持久化 | 生产环境,多实例部署 |
| WorkerPool | 并发控制 + 背压 | 单机部署,需要限制资源 |
| Legacy FIFO | 可用性兜底 | 开发/测试环境,或队列组件故障 |
> [!tip] 分级降级的核心原则
>
> **每一级都是前一级功能的超集**。也就是说:
> - TaskQueue = 持久化排队 + 重试 + Consumer 消费 + WorkerPool 执行
> - WorkerPool = 有界并发 + 直接执行(跳过持久化和消费者)
> - Legacy FIFO = 最简串行队列(跳过所有高级特性)
>
> 这种设计确保降级过程是**渐进的**——功能逐步减少但服务始终可用。类比现实中的「应急灯」:市电断了 → 应急灯亮 → 最差情况还有手电筒。永远保留一条最低限度的通路。
---
### 设计哲学
> **宁可降级,也不能拒绝服务。**
三级降级的核心思想是**渐进式降级**:
1. **功能完整** — TaskQueue 提供持久化、重试、死信等高级特性
2. **性能可控** — WorkerPool 提供有界并发和用户隔离
3. **始终可用** — Legacy FIFO 确保在任何配置下都能接收任务
每一级降级都意味着功能的减少,但服务的可用性始终得到保障。前端收到 `200 OK` 后,通过 SSE 或轮询获取任务进度,对用户而言体验一致。
## 关联文档
- [[11-Consumer-Producer桥接]] — Consumer 连接 TaskQueue 和 WorkerPool
- [[02-协程池]] — WorkerPool 的背压和限流机制
- [[03-任务队列]] — TaskQueue 接口的可插拔设计
- [[00-索引]] — 返回文档总览