184 lines
5.3 KiB
Markdown
184 lines
5.3 KiB
Markdown
|
|
# 12. 三级降级策略
|
|||
|
|
|
|||
|
|
> **一句话概括**:Generate handler 实现三级降级 — TaskQueue -> WorkerPool -> Legacy FIFO,宁可降级也不拒绝服务。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 降级链总览
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
flowchart TD
|
|||
|
|
R["HTTP Request\nPOST /api/v1/generate"] --> A{"TaskQueue\n可用?"}
|
|||
|
|
|
|||
|
|
A -->|"Submit 成功"| S1["200 OK\ntaskId 返回"]
|
|||
|
|
A -->|"Submit 失败"| B{"WorkerPool\n可用?"}
|
|||
|
|
|
|||
|
|
B -->|"Submit 成功"| S2["200 OK\ntaskId 返回"]
|
|||
|
|
B -->|"ErrPoolFull"| E2["503 Service\nUnavailable"]
|
|||
|
|
B -->|"ErrUserLimit"| E3["429 Too Many\nRequests"]
|
|||
|
|
B -->|"Pool 不可用"| C{"Legacy FIFO\nQueue 可用?"}
|
|||
|
|
|
|||
|
|
C -->|"Enqueue 成功"| S3["200 OK\ntaskId 返回"]
|
|||
|
|
C -->|"Queue 不可用"| E4["500 Internal\nServer 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 | 可用性兜底 | 开发/测试环境,或队列组件故障 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 设计哲学
|
|||
|
|
|
|||
|
|
> **宁可降级,也不能拒绝服务。**
|
|||
|
|
|
|||
|
|
三级降级的核心思想是**渐进式降级**:
|
|||
|
|
|
|||
|
|
1. **功能完整** — TaskQueue 提供持久化、重试、死信等高级特性
|
|||
|
|
2. **性能可控** — WorkerPool 提供有界并发和用户隔离
|
|||
|
|
3. **始终可用** — Legacy FIFO 确保在任何配置下都能接收任务
|
|||
|
|
|
|||
|
|
每一级降级都意味着功能的减少,但服务的可用性始终得到保障。前端收到 `200 OK` 后,通过 SSE 或轮询获取任务进度,对用户而言体验一致。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 关联文档
|
|||
|
|
|
|||
|
|
| 文档 | 关系 |
|
|||
|
|
|------|------|
|
|||
|
|
| [11 - Consumer-Producer 桥接](11-consumer-producer.md) | Consumer 连接 TaskQueue 和 WorkerPool |
|
|||
|
|
| [02 - 协程池](02-worker-pool.md) | WorkerPool 的背压和限流机制 |
|
|||
|
|
| [03 - 任务队列](03-task-queue.md) | TaskQueue 接口的可插拔设计 |
|
|||
|
|
| [00 - 索引](00-index.md) | 返回文档总览 |
|