194 lines
5.9 KiB
Markdown
194 lines
5.9 KiB
Markdown
---
|
||
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-索引]] — 返回文档总览
|