vault backup: 2026-06-03 10:30:42
This commit is contained in:
+76
-49
@@ -1,62 +1,89 @@
|
|||||||
|
---
|
||||||
|
tags: [index, architecture, gen2d, overview]
|
||||||
|
create time: 2026-06-03 09:00
|
||||||
|
---
|
||||||
|
|
||||||
# Gen2D 架构讲解 — 答辩文档索引
|
# Gen2D 架构讲解 — 答辩文档索引
|
||||||
|
|
||||||
> AI 驱动的 2D 游戏素材生成工具
|
> AI 驱动的 2D 游戏素材生成工具
|
||||||
|
|
||||||
## 文档导航
|
## 概述
|
||||||
|
|
||||||
|
本文档是 Gen2D 系统架构讲解的导航索引,覆盖从整体分层架构到具体技术组件的 15 个核心知识点。通过本文档可以快速定位到任意模块的详细解读。
|
||||||
|
|
||||||
|
## 正文
|
||||||
|
|
||||||
|
### 文档导航
|
||||||
|
|
||||||
| # | 文件 | 主题 | 核心要点 |
|
| # | 文件 | 主题 | 核心要点 |
|
||||||
|---|------|------|----------|
|
|---|------|------|----------|
|
||||||
| 1 | [01-system-overview](01-system-overview.md) | 系统总览 | Gin → Handler → Service → 基础设施 → 外部依赖 |
|
| 1 | [[01-系统总览]] | 系统总览 | Gin → Handler → Service → 基础设施 → 外部依赖 |
|
||||||
| 2 | [02-worker-pool](02-worker-pool.md) | 协程池 | 有界并发、per-user 限流、背压、优雅关闭 |
|
| 2 | [[02-协程池]] | 协程池 | 有界并发、per-user 限流、背压、优雅关闭 |
|
||||||
| 3 | [03-task-queue](03-task-queue.md) | 任务队列 | 可插拔接口 + Memory / RabbitMQ 双实现 |
|
| 3 | [[03-任务队列]] | 任务队列 | 可插拔接口 + Memory / RabbitMQ 双实现 |
|
||||||
| 4 | [04-rabbitmq](04-rabbitmq.md) | RabbitMQ 集成 | AMQP 连接、持久化消息、重试/死信流程 |
|
| 4 | [[04-RabbitMQ集成]] | RabbitMQ 集成 | AMQP 连接、持久化消息、重试/死信流程 |
|
||||||
| 5 | [05-generation-pipeline](05-generation-pipeline.md) | 生成管线 | Eino 4 阶段图 + 质量回退 + 降级路径 |
|
| 5 | [[05-生成管线]] | 生成管线 | Eino 4 阶段图 + 质量回退 + 降级路径 |
|
||||||
| 6 | [06-sprite-processing](06-sprite-processing.md) | 精灵图处理 | 背景移除 → 投影切割 → 后处理 → GIF 预览 |
|
| 6 | [[06-精灵图处理]] | 精灵图处理 | 背景移除 → 投影切割 → 后处理 → GIF 预览 |
|
||||||
| 7 | [07-observability](07-observability.md) | 可观测性 | 35 Prometheus 指标 + 3 Grafana 仪表盘 + 10 告警 |
|
| 7 | [[07-可观测性]] | 可观测性 | 35 Prometheus 指标 + 3 Grafana 仪表盘 + 10 告警 |
|
||||||
| 8 | [08-sse-push](08-sse-push.md) | SSE 实时推送 | EventBus → SSEHandler → 浏览器 EventSource |
|
| 8 | [[08-SSE实时推送]] | SSE 实时推送 | EventBus → SSEHandler → 浏览器 EventSource |
|
||||||
| 9 | [09-rate-limiting](09-rate-limiting.md) | 限流 | Redis Lua 令牌桶 + 双层限流 + Fail-Open |
|
| 9 | [[09-限流]] | 限流 | Redis Lua 令牌桶 + 双层限流 + Fail-Open |
|
||||||
| 10 | [10-middleware-chain](10-middleware-chain.md) | 中间件链 | Logger → Recovery → Metrics → Auth → RateLimit |
|
| 10 | [[10-中间件链]] | 中间件链 | Logger → Recovery → Metrics → Auth → RateLimit |
|
||||||
| 11 | [11-consumer-producer](11-consumer-producer.md) | Consumer-Producer 桥接 | TaskQueue → Consumer → WorkerPool 解耦 |
|
| 11 | [[11-Consumer-Producer桥接]] | Consumer-Producer 桥接 | TaskQueue → Consumer → WorkerPool 解耦 |
|
||||||
| 12 | [12-three-tier-fallback](12-three-tier-fallback.md) | 三级降级策略 | Queue → Pool → Legacy 降级链 |
|
| 12 | [[12-三级降级策略]] | 三级降级策略 | Queue → Pool → Legacy 降级链 |
|
||||||
| 13 | [13-prompt-engineering](13-prompt-engineering.md) | 标签驱动提示词 | 40+ 标签映射 + 风格一致性 |
|
| 13 | [[13-标签驱动提示词工程]] | 标签驱动提示词 | 40+ 标签映射 + 风格一致性 |
|
||||||
| 14 | [14-deployment](14-deployment.md) | 部署架构 | Docker Compose + Prometheus + Grafana |
|
| 14 | [[14-部署架构]] | 部署架构 | Docker Compose + Prometheus + Grafana |
|
||||||
| 15 | [15-config-cascade](15-config-cascade.md) | 配置级联 | Viper 三层配置:YAML → ENV → Default |
|
| 15 | [[15-配置级联机制]] | 配置级联 | Viper 三层配置:YAML → ENV → Default |
|
||||||
|
|
||||||
## 架构总览图
|
> [!tip] 阅读顺序建议
|
||||||
|
>
|
||||||
|
> 建议按编号顺序阅读:先理解 [[01-系统总览]] 的全局架构,再深入各个具体组件(协程池、任务队列、RabbitMQ),最后学习运行时相关话题(SSE 推送、可观测性、降级策略)。
|
||||||
|
|
||||||
```
|
### 架构总览图
|
||||||
┌─────────────────────────────────────────────────────────────────┐
|
|
||||||
│ 浏览器 (React) │
|
```mermaid
|
||||||
│ EventSource ← SSE ← EventBus ← Pipeline Progress │
|
graph TB
|
||||||
└────────────────────────────┬────────────────────────────────────┘
|
subgraph Browser["浏览器 (React)"]
|
||||||
│ HTTP
|
UI["三栏工作台"]
|
||||||
┌────────────────────────────▼────────────────────────────────────┐
|
SE["EventSource ← SSE"]
|
||||||
│ Gin HTTP Server │
|
end
|
||||||
│ Logger → Recovery → Metrics → Auth → RateLimit → Handler │
|
|
||||||
└────────────────────────────┬────────────────────────────────────┘
|
subgraph Gateway["Gin HTTP Server"]
|
||||||
│
|
MW["中间件链<br/>Logger / Recovery / Metrics / Auth / RateLimit"]
|
||||||
┌────────────────────────────▼────────────────────────────────────┐
|
end
|
||||||
│ Handler 层 │
|
|
||||||
│ GenerateHandler / SSEHandler / PromptHandler │
|
subgraph Handler["Handler 层"]
|
||||||
└──────┬─────────────────────┬─────────────────────┬──────────────┘
|
GH["GenerateHandler"]
|
||||||
│ │ │
|
SH["SSEHandler"]
|
||||||
┌──────▼──────┐ ┌─────────▼─────────┐ ┌─────▼──────┐
|
PH["PromptHandler"]
|
||||||
│ TaskQueue │ │ WorkerPool │ │ EventBus │
|
end
|
||||||
│ Memory/RMQ │───→│ NumCPU*4 并发 │ │ Pub/Sub │
|
|
||||||
└─────────────┘ │ Per-user 限流 │ └────────────┘
|
subgraph Infra["基础设施层"]
|
||||||
└─────────┬─────────┘
|
TQ["TaskQueue<br/>Memory / RabbitMQ"]
|
||||||
│
|
WP["WorkerPool<br/>NumCPU*4 并发<br/>Per-user 限流"]
|
||||||
┌─────────▼─────────┐
|
EB["EventBus<br/>Pub/Sub"]
|
||||||
│ Eino Pipeline │
|
end
|
||||||
│ 4 阶段生成管线 │
|
|
||||||
└─────────┬─────────┘
|
subgraph Pipeline["Service 层"]
|
||||||
│
|
EP["Eino Pipeline<br/>4 阶段生成管线"]
|
||||||
┌──────────────┼──────────────┐
|
end
|
||||||
▼ ▼ ▼
|
|
||||||
Image API Quality Check SplitSprite
|
subgraph External["外部依赖"]
|
||||||
(DALL-E 3) (GPT-4o) + GIF Maker
|
IMG["Image API<br/>DALL-E 3"]
|
||||||
|
QC["Quality Check<br/>GPT-4o"]
|
||||||
|
SP["SplitSprite + GIF Maker"]
|
||||||
|
end
|
||||||
|
|
||||||
|
UI -->|"HTTP"| GW
|
||||||
|
SE --> UI
|
||||||
|
GW --> MW
|
||||||
|
MW --> Handler
|
||||||
|
GH --> TQ
|
||||||
|
GH --> WP
|
||||||
|
SH --> EB
|
||||||
|
TQ --> WP
|
||||||
|
WP --> EP
|
||||||
|
EP --> IMG & QC & SP
|
||||||
```
|
```
|
||||||
|
|
||||||
## 配套图表
|
### 配套图表
|
||||||
|
|
||||||
每份文档开头使用 Mermaid 流程图,可直接在 Markdown 渲染器中查看。
|
每份文档开头使用 Mermaid 流程图,可直接在 Markdown 渲染器中查看。
|
||||||
|
|||||||
+61
-38
@@ -1,8 +1,19 @@
|
|||||||
# 01 - 系统总览
|
---
|
||||||
|
tags: [architecture, system-design, go, gin, dependency-injection, viper, config]
|
||||||
|
create time: 2026-06-03 10:00
|
||||||
|
---
|
||||||
|
|
||||||
> **一句话概括**:Gen2D 采用经典分层架构,Gin HTTP Server -> Handler -> Service -> 基础设施 -> 外部依赖,各层职责清晰、可独立替换。
|
# 01. 系统总览
|
||||||
|
|
||||||
## 架构全景
|
## 概述
|
||||||
|
|
||||||
|
Gen2D 采用经典分层架构,数据流自上而下贯穿 Gin HTTP Server → Handler → Service → 基础设施 → 外部依赖,各层职责清晰、可独立替换。
|
||||||
|
|
||||||
|
> **一句话概括**:分层架构,职责分离,轻量 DI,零配置可启动。
|
||||||
|
|
||||||
|
## 正文
|
||||||
|
|
||||||
|
### 架构全景
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
graph TB
|
graph TB
|
||||||
@@ -57,7 +68,7 @@ graph TB
|
|||||||
Pipeline -->|"Progress"| EB
|
Pipeline -->|"Progress"| EB
|
||||||
```
|
```
|
||||||
|
|
||||||
## 分层详解
|
### 分层详解
|
||||||
|
|
||||||
| 层级 | 目录 | 核心职责 | 代表组件 |
|
| 层级 | 目录 | 核心职责 | 代表组件 |
|
||||||
|------|------|----------|----------|
|
|------|------|----------|----------|
|
||||||
@@ -70,9 +81,9 @@ graph TB
|
|||||||
| **精灵处理** | `pkg/splitsprite/`, `pkg/gifmaker/` | 精灵表切割、GIF 预览生成 | `splitsprite.Process`, `gifmaker.Encode` |
|
| **精灵处理** | `pkg/splitsprite/`, `pkg/gifmaker/` | 精灵表切割、GIF 预览生成 | `splitsprite.Process`, `gifmaker.Encode` |
|
||||||
| **配置** | `internal/config/` | YAML 加载、环境变量绑定、默认值 | `config.Load()` |
|
| **配置** | `internal/config/` | YAML 加载、环境变量绑定、默认值 | `config.Load()` |
|
||||||
|
|
||||||
## 依赖注入模式
|
### 依赖注入模式
|
||||||
|
|
||||||
Gen2D 采用轻量级的 **Set\*/Init\* 函数注入** 模式,避免引入 DI 框架。
|
Gen2D 采用轻量级的 **Set*/Init* 函数注入** 模式,避免引入 DI 框架。
|
||||||
|
|
||||||
```
|
```
|
||||||
main.go 中的注入链路:
|
main.go 中的注入链路:
|
||||||
@@ -93,9 +104,11 @@ eventbus.Init() → 初始化事件总线
|
|||||||
- `service.InitImageGenConfig()` 将配置缓存为包级变量,避免在函数签名中传递大量参数
|
- `service.InitImageGenConfig()` 将配置缓存为包级变量,避免在函数签名中传递大量参数
|
||||||
- 每个 `Set*` 函数对应一个包级全局变量,简单但足够清晰
|
- 每个 `Set*` 函数对应一个包级全局变量,简单但足够清晰
|
||||||
|
|
||||||
> :bulb: **为什么不用 Wire / Fx?** 项目规模可控,`cmd/main.go` 约 220 行即可完成全部注入,框架级 DI 的复杂度收益比不高。
|
> [!tip] 为什么不用 Wire / Fx?
|
||||||
|
>
|
||||||
|
> 项目规模可控,`cmd/main.go` 约 220 行即可完成全部注入,框架级 DI 的复杂度收益比不高。
|
||||||
|
|
||||||
## 配置级联
|
### 配置级联
|
||||||
|
|
||||||
Gen2D 使用 Viper 实现三层配置覆盖,优先级从高到低:
|
Gen2D 使用 Viper 实现三层配置覆盖,优先级从高到低:
|
||||||
|
|
||||||
@@ -130,40 +143,50 @@ graph LR
|
|||||||
| `workerpool` | `GEN2D_WORKERPOOL_*` | workers, queue_size, max_per_user |
|
| `workerpool` | `GEN2D_WORKERPOOL_*` | workers, queue_size, max_per_user |
|
||||||
| `taskqueue` | `GEN2D_TASKQUEUE_*` | driver, memory.buffer_size, rabbitmq.* |
|
| `taskqueue` | `GEN2D_TASKQUEUE_*` | driver, memory.buffer_size, rabbitmq.* |
|
||||||
|
|
||||||
## 请求全链路
|
### 请求全链路
|
||||||
|
|
||||||
一个素材生成请求的完整生命周期:
|
一个素材生成请求的完整生命周期:
|
||||||
|
|
||||||
```
|
```mermaid
|
||||||
1. 浏览器 → POST /api/v1/generate
|
graph LR
|
||||||
2. Gin 中间件链 → Logger → Recovery → Metrics → Auth → RateLimit
|
BROWSER["浏览器<br/>POST /api/v1/generate"] --> GIN["Gin中间件链"]
|
||||||
3. Handler.Generate()
|
GIN --> HANDLER["Handler.Generate"]
|
||||||
├── 参数绑定 & 校验
|
HANDLER --> VALIDATE["参数绑定校验"]
|
||||||
├── 保存任务到 MySQL(status=pending)
|
HANDLER --> MYSQL["保存任务MySQL<br/>status=pending"]
|
||||||
├── 构建 TaskMessage
|
HANDLER --> MSG["构建TaskMessage"]
|
||||||
└── 提交到 TaskQueue(优先)或 WorkerPool(fallback)
|
MSG --> TQ{"提交目标"}
|
||||||
4. 返回 { taskId } 给浏览器
|
TQ -->|"优先"| TASKQUEUE["TaskQueue队列排队"]
|
||||||
5. Consumer 从 TaskQueue 消费消息
|
TQ -->|"Fallback"| WORKERPOOL["WorkerPool直连"]
|
||||||
6. Consumer → WorkerPool.Submit()
|
VALIDATE --> TQ
|
||||||
7. WorkerPool 的 Worker 执行任务:
|
MYSQL --> TQ
|
||||||
├── 注入 ProgressReporter 到 context
|
TASKQUEUE --> CONSUMER["Consumer消费"]
|
||||||
├── RunPipeline() — Eino Graph 执行
|
WORKERPOOL --> SUBMIT["WorkerPool.Submit"]
|
||||||
│ ├── PromptOptimizer(合并风格 + 技术参数)
|
CONSUMER --> SUBMIT
|
||||||
│ ├── AssetGenerator(调用 AI 出图 API)
|
SUBMIT --> WORKER["Worker执行"]
|
||||||
│ ├── QualitySupervisor(质检,最多重试 3 次)
|
WORKER --> PROGRESS["注入ProgressReporter"]
|
||||||
│ └── FormatAdapter(精灵表切割 + GIF 预览)
|
WORKER --> PIPELINE["Eino Pipeline执行"]
|
||||||
├── 上传素材到七牛云
|
PIPELINE --> PROMPT["PromptOptimizer"]
|
||||||
└── 更新 MySQL 任务状态
|
PIPELINE --> ASSET["AssetGenerator"]
|
||||||
8. EventBus.Publish() → SSE 推送给浏览器
|
PIPELINE --> QUALITY["QualitySupervisor<br/>质检最多重试3次"]
|
||||||
9. 浏览器通过 EventSource 实时接收进度
|
PIPELINE --> FORMAT["FormatAdapter<br/>精灵表切割GIF预览"]
|
||||||
|
PROGRESS --> UPLOAD["上传素材七牛云"]
|
||||||
|
QUALITY --> UPLOAD
|
||||||
|
UPLOAD --> STATUS["更新MySQL状态"]
|
||||||
|
STATUS --> EVENTBUS["EventBus.Publish"]
|
||||||
|
EVENTBUS --> SSE["SSE推送"]
|
||||||
|
SSE --> BROWSER_SSE["浏览器EventSource<br/>实时接收进度"]
|
||||||
```
|
```
|
||||||
|
|
||||||
## 关键设计决策
|
### 关键设计决策
|
||||||
|
|
||||||
|
> [!question] 思考:为什么 Gen2D 选择了异步任务 + SSE 推送的组合?
|
||||||
|
>
|
||||||
|
> 如果直接同步调用 Eino Pipeline,一个生成请求可能要等待 10~120 秒。在 HTTP 模型下,长时间占用的连接会耗尽服务器的并发能力。**异步提交 + SSE 推送**把「等待时间」从连接持有中解放出来——客户端收到 taskId 后可以自由离开,后续通过 SSE 长连接接收进度更新。这也是 Web 应用在 AI 场景下的标准模式。
|
||||||
|
|
||||||
| 决策 | 选择 | 理由 |
|
| 决策 | 选择 | 理由 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
| 分层架构 | Handler-Service-Infra 三层 | 解耦各层职责,便于独立测试和替换 |
|
| 分层架构 | Handler-Service-Infra 三层 | 解耦各层职责,便于独立测试和替换 |
|
||||||
| 依赖注入 | Set\*/Init\* 函数 | 轻量、零依赖,项目规模可控 |
|
| 依赖注入 | Set*/Init* 函数 | 轻量、零依赖,项目规模可控 |
|
||||||
| 配置管理 | Viper 三层级联 | 容器化友好,零配置可启动 |
|
| 配置管理 | Viper 三层级联 | 容器化友好,零配置可启动 |
|
||||||
| 异步任务 | 提交-队列-消费-执行 | API 快速返回,长任务不阻塞请求 |
|
| 异步任务 | 提交-队列-消费-执行 | API 快速返回,长任务不阻塞请求 |
|
||||||
| 事件推送 | EventBus + SSE | 比 WebSocket 轻量,HTTP 原生支持 |
|
| 事件推送 | EventBus + SSE | 比 WebSocket 轻量,HTTP 原生支持 |
|
||||||
@@ -171,8 +194,8 @@ graph LR
|
|||||||
|
|
||||||
## 关联文档
|
## 关联文档
|
||||||
|
|
||||||
- [索引](00-index.md) — 文档导航与架构总览图
|
- [[00-索引]] — 文档导航与架构总览图
|
||||||
- [协程池](02-worker-pool.md) — 有界并发与 per-user 限流
|
- [[02-协程池]] — 有界并发与 per-user 限流
|
||||||
- [任务队列](03-task-queue.md) — 可插拔队列接口与双实现
|
- [[03-任务队列]] — 可插拔队列接口与双实现
|
||||||
- [RabbitMQ 集成](04-rabbitmq.md) — 持久化消息与重试机制
|
- [[04-RabbitMQ集成]] — 持久化消息与重试机制
|
||||||
- [生成管线](05-generation-pipeline.md) — Eino 4 阶段管线与质量回退
|
- [[05-生成管线]] — Eino 4 阶段管线与质量回退
|
||||||
|
|||||||
+65
-63
@@ -1,22 +1,33 @@
|
|||||||
# 02 - 协程池 (Worker Pool)
|
---
|
||||||
|
tags: [concurrency, goroutine-pool, backpressure, go, worker-pattern, rate-limiting]
|
||||||
|
create time: 2026-06-03 10:05
|
||||||
|
---
|
||||||
|
|
||||||
|
# 02. 协程池 (Worker Pool)
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
有界并发协程池,以 `NumCPU*4` 个 Worker 并行处理任务,配合 per-user 限流和背压保护,支持优雅关闭。
|
||||||
|
|
||||||
> **一句话概括**:有界并发协程池,`NumCPU*4` workers,per-user 限流,背压保护,优雅关闭。
|
> **一句话概括**:有界并发协程池,`NumCPU*4` workers,per-user 限流,背压保护,优雅关闭。
|
||||||
|
|
||||||
## 工作流
|
## 正文
|
||||||
|
|
||||||
|
### 工作流
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
graph TB
|
graph TB
|
||||||
subgraph Submit["Submit() 入口"]
|
subgraph Submit["Submit入口"]
|
||||||
CHECK_CLOSED{"池已关闭?"}
|
CHECK_CLOSED{"池已关闭?"}
|
||||||
CHECK_USER{"per-user 限流<br/>active >= max?"}
|
CHECK_USER{"per-user限流<br/>active >= max?"}
|
||||||
CHECK_FULL{"channel 满?"}
|
CHECK_FULL{"channel满?"}
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph Channel["有界任务队列"]
|
subgraph Channel["有界任务队列"]
|
||||||
TASK_CHAN["chan Task<br/>capacity = queueSize"]
|
TASK_CHAN["chan Task<br/>capacity = queueSize"]
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph Workers["Worker 协程"]
|
subgraph Workers["Worker协程"]
|
||||||
W1["Worker 0"]
|
W1["Worker 0"]
|
||||||
W2["Worker 1"]
|
W2["Worker 1"]
|
||||||
W3["Worker ..."]
|
W3["Worker ..."]
|
||||||
@@ -25,7 +36,7 @@ graph TB
|
|||||||
|
|
||||||
subgraph Execute["任务执行"]
|
subgraph Execute["任务执行"]
|
||||||
TIMEOUT["context.WithTimeout<br/>10 min"]
|
TIMEOUT["context.WithTimeout<br/>10 min"]
|
||||||
FN["task.Fn(ctx)"]
|
FN["task.Fnctx"]
|
||||||
RELEASE["释放用户槽位"]
|
RELEASE["释放用户槽位"]
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -33,13 +44,13 @@ graph TB
|
|||||||
CHECK_CLOSED -->|否| CHECK_USER
|
CHECK_CLOSED -->|否| CHECK_USER
|
||||||
CHECK_USER -->|"ErrUserLimitReached"| REJECT
|
CHECK_USER -->|"ErrUserLimitReached"| REJECT
|
||||||
CHECK_USER -->|通过| CHECK_FULL
|
CHECK_USER -->|通过| CHECK_FULL
|
||||||
CHECK_FULL -->|"ErrPoolFull (HTTP 503)"| REJECT
|
CHECK_FULL -->|"ErrPoolFull HTTP 503"| REJECT
|
||||||
CHECK_FULL -->|通过| TASK_CHAN
|
CHECK_FULL -->|通过| TASK_CHAN
|
||||||
TASK_CHAN --> W1 & W2 & W3 & WN
|
TASK_CHAN --> W1 & W2 & W3 & WN
|
||||||
W1 & W2 & W3 & WN --> TIMEOUT --> FN --> RELEASE
|
W1 & W2 & W3 & WN --> TIMEOUT --> FN --> RELEASE
|
||||||
```
|
```
|
||||||
|
|
||||||
## Pool 结构体
|
### Pool 结构体
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type Pool struct {
|
type Pool struct {
|
||||||
@@ -70,7 +81,7 @@ type Pool struct {
|
|||||||
| `taskQueue` | `chan Task` | buffered channel | 有界队列,固定容量 |
|
| `taskQueue` | `chan Task` | buffered channel | 有界队列,固定容量 |
|
||||||
| `userActive` | `map[string]int` | — | 记录每用户活跃任务数 |
|
| `userActive` | `map[string]int` | — | 记录每用户活跃任务数 |
|
||||||
|
|
||||||
## Functional Options 模式
|
### Functional Options 模式
|
||||||
|
|
||||||
协程池采用 Functional Options 模式进行配置,开箱即用、可选覆盖:
|
协程池采用 Functional Options 模式进行配置,开箱即用、可选覆盖:
|
||||||
|
|
||||||
@@ -88,9 +99,11 @@ pool := workerpool.New(
|
|||||||
| `WithQueueSize(n)` | `100` | `n < 1` 时强制为 1 | 有界缓冲,满时触发背压 |
|
| `WithQueueSize(n)` | `100` | `n < 1` 时强制为 1 | 有界缓冲,满时触发背压 |
|
||||||
| `WithMaxPerUser(n)` | `2` | `n < 1` 时置 0(不限制) | 防止单用户占满池 |
|
| `WithMaxPerUser(n)` | `2` | `n < 1` 时置 0(不限制) | 防止单用户占满池 |
|
||||||
|
|
||||||
> :bulb: **为什么选择 Functional Options?** 可选参数天然为零值时保持默认,新增配置项无需修改 `New()` 签名,调用方按需指定。
|
> [!tip] 为什么选择 Functional Options?
|
||||||
|
>
|
||||||
|
> 可选参数天然为零值时保持默认,新增配置项无需修改 `New()` 签名,调用方按需指定。
|
||||||
|
|
||||||
## 有界并发
|
### 有界并发
|
||||||
|
|
||||||
协程池的核心是 `make(chan Task, queueSize)` 创建的 **有界缓冲 channel**。
|
协程池的核心是 `make(chan Task, queueSize)` 创建的 **有界缓冲 channel**。
|
||||||
|
|
||||||
@@ -112,7 +125,7 @@ default: // 队列满,背压
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Per-user 限流
|
### Per-user 限流
|
||||||
|
|
||||||
每个用户同时执行的任务数受到 `maxPerUser` 限制,防止单用户占满整个池。
|
每个用户同时执行的任务数受到 `maxPerUser` 限制,防止单用户占满整个池。
|
||||||
|
|
||||||
@@ -134,55 +147,50 @@ Submit() 调用流程:
|
|||||||
| Execute | — | Worker 从 channel 取出后开始执行 |
|
| Execute | — | Worker 从 channel 取出后开始执行 |
|
||||||
| Complete | `defer userActive[userID]--` | 任务完成或失败时释放 |
|
| Complete | `defer userActive[userID]--` | 任务完成或失败时释放 |
|
||||||
|
|
||||||
> :warning: **槽位预留时机**:在 `Submit()` 而非 `worker()` 中预留,确保 channel 满时不会出现"槽位已分配但任务未入队"的不一致状态。
|
> [!warning] 槽位预留时机
|
||||||
|
>
|
||||||
|
> 在 `Submit()` 而非 `worker()` 中预留,确保 channel 满时不会出现"槽位已分配但任务未入队"的不一致状态。
|
||||||
|
|
||||||
## 背压保护
|
### 背压保护
|
||||||
|
|
||||||
当任务队列已满时,协程池通过 `select + default` 实现非阻塞拒绝:
|
当任务队列已满时,协程池通过 `select + default` 实现非阻塞拒绝:
|
||||||
|
|
||||||
```
|
```mermaid
|
||||||
队列满(channel 已达 capacity)
|
graph TB
|
||||||
│
|
FULL["队列满channel已达capacity"] --> SELECT["select进入default分支"]
|
||||||
▼
|
SELECT --> ROLLBACK["回滚用户槽位如果有"]
|
||||||
select 进入 default 分支
|
SELECT --> INC["原子递增RejectedTasks"]
|
||||||
│
|
SELECT --> RETURN["返回ErrPoolFull"]
|
||||||
├── 回滚用户槽位(如果有)
|
RETURN --> HTTP503["Handler层映射为HTTP 503<br/>响应体系统繁忙请稍后重试"]
|
||||||
├── 原子递增 RejectedTasks
|
|
||||||
└── 返回 ErrPoolFull
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Handler 层映射为 HTTP 503 Service Unavailable
|
|
||||||
响应体:"系统繁忙,请稍后重试"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**背压 vs 阻塞**:
|
**背压 vs 阻塞**:
|
||||||
|
|
||||||
| 策略 | 行为 | 适用场景 |
|
| 策略 | 行为 | 适用场景 |
|
||||||
|------|------|----------|
|
|------|------|----------|
|
||||||
| 非阻塞拒绝(Gen2D) | 立即返回错误 | 用户交互型 API,快速失败 |
|
| 非阻塞拒绝Gen2D | 立即返回错误 | 用户交互型 API,快速失败 |
|
||||||
| 阻塞等待 | 阻塞直到有空位 | 批处理系统,不能丢任务 |
|
| 阻塞等待 | 阻塞直到有空位 | 批处理系统,不能丢任务 |
|
||||||
|
|
||||||
Gen2D 选择非阻塞拒绝,因为用户期望快速得到反馈,而非无限等待。
|
Gen2D 选择非阻塞拒绝,因为用户期望快速得到反馈,而非无限等待。
|
||||||
|
|
||||||
## 优雅关闭
|
> [!tip] 设计权衡
|
||||||
|
>
|
||||||
|
> 选择「非阻塞拒绝」意味着可能丢失用户的提交意图。但在 AI 生成场景中,用户可以 **重新点击提交按钮**——这种短暂的操作成本远低于服务器因连接堆积导致的雪崩风险。这就是经典的 **「可用性 > 一致性」** 抉择。
|
||||||
|
|
||||||
|
### 优雅关闭
|
||||||
|
|
||||||
协程池支持优雅关闭,确保正在执行的任务有时间完成:
|
协程池支持优雅关闭,确保正在执行的任务有时间完成:
|
||||||
|
|
||||||
```
|
```mermaid
|
||||||
收到 SIGINT / SIGTERM
|
graph TB
|
||||||
│
|
SIG["收到SIGINT / SIGTERM"] --> STOP_CONSUME["consumeCancel<br/>停止消费者不再接收新任务"]
|
||||||
▼
|
STOP_CONSUME --> SHUTDOWN["pool.Shutdownctx<br/>30秒超时"]
|
||||||
consumeCancel() ← 停止消费者,不再接收新任务
|
SHUTDOWN --> SWAP["closed.Swaptrue<br/>停止接收新任务"]
|
||||||
│
|
SHUTDOWN --> CANCEL["cancel<br/>通知Worker停止取任务"]
|
||||||
▼
|
SHUTDOWN --> WAIT["wg.Wait<br/>等待所有Worker退出"]
|
||||||
pool.Shutdown(ctx) ← 30 秒超时
|
WAIT --> SUCCESS{"成功?"}
|
||||||
│
|
SUCCESS -->|是| LOG_OK["日志workerpool shutdown gracefully"]
|
||||||
├── closed.Swap(true) ← 停止接收新任务
|
SUCCESS -->|否| LOG_TIMEOUT["日志workerpool shutdown timeout"]
|
||||||
├── cancel() ← 通知 Worker 停止取任务
|
|
||||||
├── wg.Wait() ← 等待所有 Worker 退出
|
|
||||||
│
|
|
||||||
├── 成功 → 日志 "workerpool shutdown gracefully"
|
|
||||||
└── 超时 → 日志 "workerpool shutdown timeout"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Shutdown 返回值**:
|
**Shutdown 返回值**:
|
||||||
@@ -192,7 +200,7 @@ pool.Shutdown(ctx) ← 30 秒超时
|
|||||||
| `true` | 所有任务正常完成 |
|
| `true` | 所有任务正常完成 |
|
||||||
| `false` | 超时,部分任务可能丢失 |
|
| `false` | 超时,部分任务可能丢失 |
|
||||||
|
|
||||||
## Metrics 指标
|
### Metrics 指标
|
||||||
|
|
||||||
协程池内置原子计数器,支持运行时观测:
|
协程池内置原子计数器,支持运行时观测:
|
||||||
|
|
||||||
@@ -207,7 +215,7 @@ pool.Shutdown(ctx) ← 30 秒超时
|
|||||||
|
|
||||||
所有指标通过 `Metrics()` 方法返回只读快照,同时上报 Prometheus。
|
所有指标通过 `Metrics()` 方法返回只读快照,同时上报 Prometheus。
|
||||||
|
|
||||||
## Task 结构体
|
### Task 结构体
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type Task struct {
|
type Task struct {
|
||||||
@@ -220,19 +228,13 @@ type Task struct {
|
|||||||
|
|
||||||
每个任务携带超时 context(默认 10 分钟),从池的根 context 派生,确保优雅关闭时能取消正在执行的任务。
|
每个任务携带超时 context(默认 10 分钟),从池的根 context 派生,确保优雅关闭时能取消正在执行的任务。
|
||||||
|
|
||||||
## 与 TaskQueue 的协作
|
### 与 TaskQueue 的协作
|
||||||
|
|
||||||
```
|
```mermaid
|
||||||
TaskQueue(全局排队)
|
graph LR
|
||||||
│
|
TQ["TaskQueue全局排队"] --> CONSUMER["Consumer消费消息"]
|
||||||
▼
|
CONSUMER --> WP["WorkerPoolSubmit单机并发控制"]
|
||||||
Consumer(消费消息)
|
WP --> WORKER["Worker执行Pipeline"]
|
||||||
│
|
|
||||||
▼
|
|
||||||
WorkerPool.Submit()(单机并发控制)
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
Worker 执行 Pipeline
|
|
||||||
```
|
```
|
||||||
|
|
||||||
| 组件 | 职责 | 范围 |
|
| 组件 | 职责 | 范围 |
|
||||||
@@ -243,7 +245,7 @@ Worker 执行 Pipeline
|
|||||||
|
|
||||||
## 关联文档
|
## 关联文档
|
||||||
|
|
||||||
- [索引](00-index.md) — 文档导航与架构总览图
|
- [[00-索引]] — 文档导航与架构总览图
|
||||||
- [系统总览](01-system-overview.md) — 分层架构与依赖注入
|
- [[01-系统总览]] — 分层架构与依赖注入
|
||||||
- [任务队列](03-task-queue.md) — 可插拔队列接口
|
- [[03-任务队列]] — 可插拔队列接口
|
||||||
- [Consumer-Producer 桥接](00-index.md) — TaskQueue 到 WorkerPool 的解耦
|
- [[11-Consumer-Producer桥接]] — TaskQueue 到 WorkerPool 的解耦
|
||||||
|
|||||||
+45
-28
@@ -1,19 +1,30 @@
|
|||||||
# 03 - 任务队列 (Task Queue)
|
---
|
||||||
|
tags: [task-queue, plugin-architecture, memory-queue, rabbitmq, go, interface-pattern]
|
||||||
|
create time: 2026-06-03 10:10
|
||||||
|
---
|
||||||
|
|
||||||
|
# 03. 任务队列 (Task Queue)
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
可插拔任务队列接口,支持 Memory 和 RabbitMQ 双实现,通过工厂模式一行切换,满足不同部署环境的需求。
|
||||||
|
|
||||||
> **一句话概括**:可插拔任务队列接口,支持 Memory 和 RabbitMQ 双实现,通过工厂模式一行切换。
|
> **一句话概括**:可插拔任务队列接口,支持 Memory 和 RabbitMQ 双实现,通过工厂模式一行切换。
|
||||||
|
|
||||||
## 架构设计
|
## 正文
|
||||||
|
|
||||||
|
### 架构设计
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
graph TB
|
graph TB
|
||||||
subgraph Producer["生产者"]
|
subgraph Producer["生产者"]
|
||||||
HANDLER["Handler.Generate()"]
|
HANDLER["Handler.Generate"]
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph Interface["TaskQueue 接口"]
|
subgraph Interface["TaskQueue接口"]
|
||||||
SUBMIT["Submit(ctx, msg)"]
|
SUBMIT["Submitctx msg"]
|
||||||
CONSUME["Consume(ctx, handler)"]
|
CONSUME["Consumectx handler"]
|
||||||
CLOSE["Close()"]
|
CLOSE["Close"]
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph Memory["MemoryQueue"]
|
subgraph Memory["MemoryQueue"]
|
||||||
@@ -36,7 +47,7 @@ graph TB
|
|||||||
RabbitMQ --- RMQ_PUB
|
RabbitMQ --- RMQ_PUB
|
||||||
```
|
```
|
||||||
|
|
||||||
## TaskQueue 接口
|
### TaskQueue 接口
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type TaskQueue interface {
|
type TaskQueue interface {
|
||||||
@@ -52,7 +63,7 @@ type TaskQueue interface {
|
|||||||
| `Consume` | 持续消费任务,直到 ctx 取消 | handler 返回错误时,内存队列丢弃,RabbitMQ NACK 重试 |
|
| `Consume` | 持续消费任务,直到 ctx 取消 | handler 返回错误时,内存队列丢弃,RabbitMQ NACK 重试 |
|
||||||
| `Close` | 关闭连接,释放资源 | 返回 `errors.Join` 聚合错误 |
|
| `Close` | 关闭连接,释放资源 | 返回 `errors.Join` 聚合错误 |
|
||||||
|
|
||||||
## 工厂模式
|
### 工厂模式
|
||||||
|
|
||||||
通过配置驱动,一行切换队列实现:
|
通过配置驱动,一行切换队列实现:
|
||||||
|
|
||||||
@@ -76,7 +87,7 @@ func New(cfg config.TaskQueueConfig) (TaskQueue, error) {
|
|||||||
| `"memory"` (默认) | `MemoryQueue` | 单机开发、演示环境 |
|
| `"memory"` (默认) | `MemoryQueue` | 单机开发、演示环境 |
|
||||||
| `"rabbitmq"` | `RabbitMQQueue` | 多机生产部署 |
|
| `"rabbitmq"` | `RabbitMQQueue` | 多机生产部署 |
|
||||||
|
|
||||||
## TaskMessage 消息结构
|
### TaskMessage 消息结构
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type TaskMessage struct {
|
type TaskMessage struct {
|
||||||
@@ -95,7 +106,7 @@ type TaskMessage struct {
|
|||||||
- `RetryCount` 供 RabbitMQ 实现判断是否超过最大重试次数
|
- `RetryCount` 供 RabbitMQ 实现判断是否超过最大重试次数
|
||||||
- JSON 序列化,兼容内存队列和 RabbitMQ 两种传输
|
- JSON 序列化,兼容内存队列和 RabbitMQ 两种传输
|
||||||
|
|
||||||
## MemoryQueue 实现
|
### MemoryQueue 实现
|
||||||
|
|
||||||
基于 Go channel 的内存队列,零外部依赖。
|
基于 Go channel 的内存队列,零外部依赖。
|
||||||
|
|
||||||
@@ -109,7 +120,7 @@ type MemoryQueue struct {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Submit
|
#### Submit
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func (q *MemoryQueue) Submit(ctx context.Context, msg TaskMessage) error {
|
func (q *MemoryQueue) Submit(ctx context.Context, msg TaskMessage) error {
|
||||||
@@ -124,9 +135,11 @@ func (q *MemoryQueue) Submit(ctx context.Context, msg TaskMessage) error {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
> :bulb: **阻塞语义**:MemoryQueue 的 Submit 是阻塞的——当 buffer 满时,调用方会阻塞直到有空位或 ctx 取消。这与 WorkerPool 的非阻塞拒绝形成对比。
|
> [!tip] 阻塞语义
|
||||||
|
>
|
||||||
|
> MemoryQueue 的 Submit 是阻塞的——当 buffer 满时,调用方会阻塞直到有空位或 ctx 取消。这与 WorkerPool 的非阻塞拒绝形成对比。
|
||||||
|
|
||||||
### Consume
|
#### Consume
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func (q *MemoryQueue) Consume(ctx context.Context, handler func(TaskMessage) error) error {
|
func (q *MemoryQueue) Consume(ctx context.Context, handler func(TaskMessage) error) error {
|
||||||
@@ -155,11 +168,13 @@ func (q *MemoryQueue) Consume(ctx context.Context, handler func(TaskMessage) err
|
|||||||
| 背压 | 阻塞直到有空位 |
|
| 背压 | 阻塞直到有空位 |
|
||||||
| 依赖 | 零外部依赖 |
|
| 依赖 | 零外部依赖 |
|
||||||
|
|
||||||
> :warning: **可接受的任务丢失**:AI 生成任务可以重新提交,进程重启丢失排队中的任务是可接受的权衡。
|
> [!warning] 可接受的任务丢失
|
||||||
|
>
|
||||||
|
> AI 生成任务可以重新提交,进程重启丢失排队中的任务是可接受的权衡。但如果你的业务场景中 **任务不可重放**(比如支付指令),则必须选择 RabbitMQ 等持久化实现。
|
||||||
|
|
||||||
## RabbitMQQueue 实现
|
### RabbitMQQueue 实现
|
||||||
|
|
||||||
基于 AMQP 的持久化消息队列,支持手动 ACK 和重试。详见 [04-rabbitmq](04-rabbitmq.md)。
|
基于 AMQP 的持久化消息队列,支持手动 ACK 和重试。详见 [[04-RabbitMQ集成]]。
|
||||||
|
|
||||||
**RabbitMQQueue 特性**:
|
**RabbitMQQueue 特性**:
|
||||||
|
|
||||||
@@ -170,7 +185,7 @@ func (q *MemoryQueue) Consume(ctx context.Context, handler func(TaskMessage) err
|
|||||||
| 背压 | 发布失败时返回错误(不阻塞) |
|
| 背压 | 发布失败时返回错误(不阻塞) |
|
||||||
| 依赖 | 需要 RabbitMQ 服务 |
|
| 依赖 | 需要 RabbitMQ 服务 |
|
||||||
|
|
||||||
## 双实现对比
|
### 双实现对比
|
||||||
|
|
||||||
| 维度 | MemoryQueue | RabbitMQQueue |
|
| 维度 | MemoryQueue | RabbitMQQueue |
|
||||||
|------|-------------|---------------|
|
|------|-------------|---------------|
|
||||||
@@ -182,7 +197,7 @@ func (q *MemoryQueue) Consume(ctx context.Context, handler func(TaskMessage) err
|
|||||||
| **适用** | 开发/演示 | 生产环境 |
|
| **适用** | 开发/演示 | 生产环境 |
|
||||||
| **消息丢失** | 进程重启丢失 | 服务重启不丢失 |
|
| **消息丢失** | 进程重启丢失 | 服务重启不丢失 |
|
||||||
|
|
||||||
## Consumer 桥接
|
### Consumer 桥接
|
||||||
|
|
||||||
Consumer 从 TaskQueue 消费消息,提交到 WorkerPool 执行,实现队列与并发控制的解耦:
|
Consumer 从 TaskQueue 消费消息,提交到 WorkerPool 执行,实现队列与并发控制的解耦:
|
||||||
|
|
||||||
@@ -201,12 +216,14 @@ func (c *Consumer) Start(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```mermaid
|
||||||
TaskQueue → Consumer → WorkerPool → Pipeline
|
graph LR
|
||||||
全局排队 桥接 单机并发 业务逻辑
|
TQ["TaskQueue全局排队"] --> CONSUMER["Consumer桥接"]
|
||||||
|
CONSUMER --> WP["WorkerPool单机并发"]
|
||||||
|
WP --> PIPELINE["Pipeline业务逻辑"]
|
||||||
```
|
```
|
||||||
|
|
||||||
## 三级降级策略
|
### 三级降级策略
|
||||||
|
|
||||||
Gen2D 在 `cmd/main.go` 中实现了三级降级链:
|
Gen2D 在 `cmd/main.go` 中实现了三级降级链:
|
||||||
|
|
||||||
@@ -229,7 +246,7 @@ if taskQueue != nil {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Metrics 指标
|
### Metrics 指标
|
||||||
|
|
||||||
| Prometheus 指标 | 类型 | Label | 说明 |
|
| Prometheus 指标 | 类型 | Label | 说明 |
|
||||||
|-----------------|------|-------|------|
|
|-----------------|------|-------|------|
|
||||||
@@ -241,7 +258,7 @@ if taskQueue != nil {
|
|||||||
|
|
||||||
## 关联文档
|
## 关联文档
|
||||||
|
|
||||||
- [索引](00-index.md) — 文档导航与架构总览图
|
- [[00-索引]] — 文档导航与架构总览图
|
||||||
- [系统总览](01-system-overview.md) — 分层架构与依赖注入
|
- [[01-系统总览]] — 分层架构与依赖注入
|
||||||
- [协程池](02-worker-pool.md) — 有界并发与 per-user 限流
|
- [[02-协程池]] — 有界并发与 per-user 限流
|
||||||
- [RabbitMQ 集成](04-rabbitmq.md) — 持久化消息与重试机制
|
- [[04-RabbitMQ集成]] — 持久化消息与重试机制
|
||||||
|
|||||||
+55
-48
@@ -1,17 +1,28 @@
|
|||||||
# 04 - RabbitMQ 集成
|
---
|
||||||
|
tags: [rabbitmq, amqp, message-queue, persistence, ack-nack, retry-pattern, go]
|
||||||
|
create time: 2026-06-03 10:15
|
||||||
|
---
|
||||||
|
|
||||||
|
# 04. RabbitMQ 集成
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
基于 AMQP 协议的持久化消息队列实现,支持手动 ACK/NACK、失败重试和死信丢弃,保障消息不丢失。
|
||||||
|
|
||||||
> **一句话概括**:基于 AMQP 的持久化消息队列,支持手动 ACK、失败重试和死信丢弃。
|
> **一句话概括**:基于 AMQP 的持久化消息队列,支持手动 ACK、失败重试和死信丢弃。
|
||||||
|
|
||||||
## 消息流
|
## 正文
|
||||||
|
|
||||||
|
### 消息流
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
graph TB
|
graph TB
|
||||||
subgraph Producer["生产者"]
|
subgraph Producer["生产者"]
|
||||||
HANDLER["Handler.Generate()"]
|
HANDLER["Handler.Generate"]
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph RabbitMQ["RabbitMQ"]
|
subgraph RabbitMQ["RabbitMQ"]
|
||||||
EXCHANGE["Default Exchange<br/>(Direct)"]
|
EXCHANGE["Default ExchangeDirect"]
|
||||||
QUEUE["gen2d:tasks<br/>durable=true"]
|
QUEUE["gen2d:tasks<br/>durable=true"]
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -19,15 +30,15 @@ graph TB
|
|||||||
CONSUME["channel.Consume<br/>autoAck=false"]
|
CONSUME["channel.Consume<br/>autoAck=false"]
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph Decision["ACK/NACK 决策树"]
|
subgraph Decision["ACK/NACK决策树"]
|
||||||
SUCCESS{"handler 成功?"}
|
SUCCESS{"handler成功?"}
|
||||||
RETRY{"retry < maxRetry?"}
|
RETRY{"retry lt maxRetry?"}
|
||||||
ACK_OK["ACK<br/>确认消费"]
|
ACK_OK["ACK确认消费"]
|
||||||
NACK["NACK + requeue<br/>重新入队"]
|
NACK["NACK + requeue<br/>重新入队"]
|
||||||
ACK_DISCARD["ACK (discard)<br/>丢弃死信"]
|
ACK_DISCARD["ACK discard<br/>丢弃死信"]
|
||||||
end
|
end
|
||||||
|
|
||||||
HANDLER -->|"Publish<br/>Persistent"| EXCHANGE
|
HANDLER -->|"Publish Persistent"| EXCHANGE
|
||||||
EXCHANGE --> QUEUE
|
EXCHANGE --> QUEUE
|
||||||
QUEUE --> CONSUME
|
QUEUE --> CONSUME
|
||||||
CONSUME --> SUCCESS
|
CONSUME --> SUCCESS
|
||||||
@@ -38,24 +49,16 @@ graph TB
|
|||||||
NACK -.->|"重新投递"| QUEUE
|
NACK -.->|"重新投递"| QUEUE
|
||||||
```
|
```
|
||||||
|
|
||||||
## 连接流程
|
### 连接流程
|
||||||
|
|
||||||
RabbitMQQueue 在初始化时完成连接、声明队列、设置 QoS:
|
RabbitMQQueue 在初始化时完成连接、声明队列、设置 QoS:
|
||||||
|
|
||||||
```
|
```mermaid
|
||||||
amqp.Dial(cfg.URL)
|
graph TB
|
||||||
│
|
DIAL["amqp.Dialcfg.URL"] --> CHANNEL["conn.Channel"]
|
||||||
▼
|
CHANNEL --> DECLARE["ch.QueueDeclare name durable=true"]
|
||||||
conn.Channel()
|
DECLARE --> QOS["ch.Qosprefetch=1"]
|
||||||
│
|
QOS --> RESULT["RabbitMQQueue实例"]
|
||||||
▼
|
|
||||||
ch.QueueDeclare(name, durable=true, autoDelete=false, exclusive=false)
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
ch.Qos(prefetch=1, prefetchSize=0, global=false)
|
|
||||||
│
|
|
||||||
▼
|
|
||||||
RabbitMQQueue{conn, channel, queue, maxRetry, prefetch}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**参数说明**:
|
**参数说明**:
|
||||||
@@ -67,9 +70,11 @@ RabbitMQQueue{conn, channel, queue, maxRetry, prefetch}
|
|||||||
| `Prefetch` | `1` | 每次预取消息数,1 保证公平调度 |
|
| `Prefetch` | `1` | 每次预取消息数,1 保证公平调度 |
|
||||||
| `MaxRetry` | `3` | 失败最大重试次数 |
|
| `MaxRetry` | `3` | 失败最大重试次数 |
|
||||||
|
|
||||||
> :bulb: **Prefetch=1 的含义**:每个 Consumer 同时只处理 1 条消息,处理完(ACK)后才接收下一条。这避免了消息堆积在 Consumer 端,配合协程池的并发控制实现精确的任务调度。
|
> [!tip] Prefetch=1 的含义
|
||||||
|
>
|
||||||
|
> 每个 Consumer 同时只处理 1 条消息,处理完ACK后才接收下一条。这避免了消息堆积在 Consumer 端,配合协程池的并发控制实现精确的任务调度。
|
||||||
|
|
||||||
## 消息发布 (Submit)
|
### 消息发布 (Submit)
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func (q *RabbitMQQueue) Submit(ctx context.Context, msg TaskMessage) error {
|
func (q *RabbitMQQueue) Submit(ctx context.Context, msg TaskMessage) error {
|
||||||
@@ -100,7 +105,7 @@ func (q *RabbitMQQueue) Submit(ctx context.Context, msg TaskMessage) error {
|
|||||||
| `ContentType` | `application/json` | JSON 序列化 |
|
| `ContentType` | `application/json` | JSON 序列化 |
|
||||||
| `x-retry-count` | `int` (header) | 当前重试次数,供消费端判断 |
|
| `x-retry-count` | `int` (header) | 当前重试次数,供消费端判断 |
|
||||||
|
|
||||||
## 消息消费 (Consume)
|
### 消息消费 (Consume)
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func (q *RabbitMQQueue) Consume(ctx context.Context, handler func(TaskMessage) error) error {
|
func (q *RabbitMQQueue) Consume(ctx context.Context, handler func(TaskMessage) error) error {
|
||||||
@@ -121,25 +126,25 @@ func (q *RabbitMQQueue) Consume(ctx context.Context, handler func(TaskMessage) e
|
|||||||
|
|
||||||
手动 ACK 给予消费者完全的控制权——只有当消息被成功处理后才确认,否则可以选择重试或丢弃。
|
手动 ACK 给予消费者完全的控制权——只有当消息被成功处理后才确认,否则可以选择重试或丢弃。
|
||||||
|
|
||||||
## ACK/NACK 决策树
|
### ACK/NACK 决策树
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
graph TB
|
graph TB
|
||||||
MSG["收到消息"] --> PARSE{"JSON 解析成功?"}
|
MSG["收到消息"] --> PARSE{"JSON解析成功?"}
|
||||||
PARSE -->|否| ACK_DISCARD1["ACK (discard)<br/>格式错误无法恢复"]
|
PARSE -->|否| ACK_DISCARD1["ACK discard<br/>格式错误无法恢复"]
|
||||||
PARSE -->|是| HANDLER{"handler(msg)<br/>执行成功?"}
|
PARSE -->|是| HANDLER{"handlermsg执行成功?"}
|
||||||
HANDLER -->|是| ACK_OK["ACK<br/>确认消费"]
|
HANDLER -->|是| ACK_OK["ACK确认消费"]
|
||||||
HANDLER -->|否| RETRY_CHECK{"msg.RetryCount<br/>< maxRetry?"}
|
HANDLER -->|否| RETRY_CHECK{"msg.RetryCount lt maxRetry?"}
|
||||||
RETRY_CHECK -->|是| NACK["NACK(requeue=true)<br/>重新入队等待重试"]
|
RETRY_CHECK -->|是| NACK["NACKrequeuetrue<br/>重新入队等待重试"]
|
||||||
RETRY_CHECK -->|否| ACK_DISCARD2["ACK (discard)<br/>超过最大重试,记录死信日志"]
|
RETRY_CHECK -->|否| ACK_DISCARD2["ACK discard<br/>超过最大重试记录死信日志"]
|
||||||
```
|
```
|
||||||
|
|
||||||
| 场景 | 操作 | 说明 |
|
| 场景 | 操作 | 说明 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
| handler 成功 | `d.Ack(false)` | 确认消费,消息从队列移除 |
|
| handler 成功 | `d.Ackfalse` | 确认消费,消息从队列移除 |
|
||||||
| handler 失败 + retry < max | `d.Nack(false, true)` | 拒绝并重新入队,retry count 递增 |
|
| handler 失败 + retry < max | `d.Nackfalse, true` | 拒绝并重新入队,retry count 递增 |
|
||||||
| handler 失败 + retry >= max | `d.Ack(false)` + 日志 | 超过最大重试,丢弃(可扩展为死信队列) |
|
| handler 失败 + retry >= max | `d.Ackfalse` + 日志 | 超过最大重试,丢弃可扩展为死信队列 |
|
||||||
| JSON 解析失败 | `d.Ack(false)` | 格式错误无法恢复,直接丢弃 |
|
| JSON 解析失败 | `d.Ackfalse` | 格式错误无法恢复,直接丢弃 |
|
||||||
|
|
||||||
**重试计数传递**:
|
**重试计数传递**:
|
||||||
|
|
||||||
@@ -155,9 +160,11 @@ if retry, ok := d.Headers["x-retry-count"].(int32); ok {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
> :warning: **NACK requeue 的行为**:`Nack(false, true)` 会将消息重新放回队列头部。如果消费者立即再次消费,可能导致"毒消息"反复重试。Gen2D 通过 `maxRetry=3` 限制重试次数,并在超过后 ACK 丢弃来规避此问题。
|
> [!warning] NACK requeue 的行为
|
||||||
|
>
|
||||||
|
> `Nack(false, true)` 会将消息重新放回队列头部。如果消费者立即再次消费,可能导致"毒消息"反复重试。Gen2D 通过 `maxRetry=3` 限制重试次数,并在超过后 ACK 丢弃来规避此问题。
|
||||||
|
|
||||||
## Metrics 指标
|
### Metrics 指标
|
||||||
|
|
||||||
| Prometheus 指标 | 类型 | Label | 说明 |
|
| Prometheus 指标 | 类型 | Label | 说明 |
|
||||||
|-----------------|------|-------|------|
|
|-----------------|------|-------|------|
|
||||||
@@ -167,7 +174,7 @@ if retry, ok := d.Headers["x-retry-count"].(int32); ok {
|
|||||||
| `gen2d_queue_errors_total` | Counter | `driver=rabbitmq`, `error_type` | 错误总量 |
|
| `gen2d_queue_errors_total` | Counter | `driver=rabbitmq`, `error_type` | 错误总量 |
|
||||||
| `gen2d_queue_submit_duration_seconds` | Histogram | `driver=rabbitmq` | 发布耗时 |
|
| `gen2d_queue_submit_duration_seconds` | Histogram | `driver=rabbitmq` | 发布耗时 |
|
||||||
|
|
||||||
## 关闭流程
|
### 关闭流程
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func (q *RabbitMQQueue) Close() error {
|
func (q *RabbitMQQueue) Close() error {
|
||||||
@@ -181,7 +188,7 @@ func (q *RabbitMQQueue) Close() error {
|
|||||||
|
|
||||||
**关闭顺序**:Channel 先于 Connection 关闭,确保所有未确认的消息被释放回队列。
|
**关闭顺序**:Channel 先于 Connection 关闭,确保所有未确认的消息被释放回队列。
|
||||||
|
|
||||||
## 配置参考
|
### 配置参考
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# config.yaml
|
# config.yaml
|
||||||
@@ -203,7 +210,7 @@ taskqueue:
|
|||||||
|
|
||||||
## 关联文档
|
## 关联文档
|
||||||
|
|
||||||
- [索引](00-index.md) — 文档导航与架构总览图
|
- [[00-索引]] — 文档导航与架构总览图
|
||||||
- [任务队列](03-task-queue.md) — 可插拔接口与 MemoryQueue 实现
|
- [[03-任务队列]] — 可插拔接口与 MemoryQueue 实现
|
||||||
- [协程池](02-worker-pool.md) — 单机并发控制
|
- [[02-协程池]] — 单机并发控制
|
||||||
- [系统总览](01-system-overview.md) — 分层架构与配置级联
|
- [[01-系统总览]] — 分层架构与配置级联
|
||||||
|
|||||||
+27
-7
@@ -1,6 +1,17 @@
|
|||||||
# 05 - 生成管线 (Generation Pipeline)
|
---
|
||||||
|
tags: [pipeline, eino, graph-pattern, quality-check, fallback, state-machine, go]
|
||||||
|
create time: 2026-06-03 10:20
|
||||||
|
---
|
||||||
|
|
||||||
> **一句话概括**:基于 CloudWeGo Eino 的 4 阶段生成管线,带质量回退和降级,确保任务不阻塞。
|
# 05. 生成管线 (Generation Pipeline)
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
基于 CloudWeGo Eino 的 4 阶段生成管线,带质量回退和降级,确保任务不阻塞。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 正文
|
||||||
|
|
||||||
## 管线拓扑
|
## 管线拓扑
|
||||||
|
|
||||||
@@ -161,6 +172,15 @@ if imgCfg.APIKey == "" {
|
|||||||
4. pass=false + RetryCount >= 3 → NextNode = "format_adapter"(降级)
|
4. pass=false + RetryCount >= 3 → NextNode = "format_adapter"(降级)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> [!tip] 重试策略思考
|
||||||
|
>
|
||||||
|
> **为什么是 3 次?**
|
||||||
|
> - 第 1 次失败:LLM API 波动或偶发噪声,重试大概率通过
|
||||||
|
> - 第 2 次失败:提示词可能不够精确,重新优化后改善
|
||||||
|
> - 第 3 次仍失败:当前参数组合确实无法生成合格图片,继续重试只会浪费资源
|
||||||
|
>
|
||||||
|
> 超过 3 次后 **降级到 FormatAdapter**——即使结果不完美,也比永远阻塞管线要好。这就是「宁可降级,不可阻塞」的原则。
|
||||||
|
|
||||||
**路由分支**(Eino `AddBranch`):
|
**路由分支**(Eino `AddBranch`):
|
||||||
|
|
||||||
```go
|
```go
|
||||||
@@ -185,7 +205,7 @@ g.AddBranch(nodeQualitySupervisor, compose.NewGraphBranch(
|
|||||||
| `NewCountedQualityChecker(n)` | 第 n 次调用后通过 | 测试重试逻辑 |
|
| `NewCountedQualityChecker(n)` | 第 n 次调用后通过 | 测试重试逻辑 |
|
||||||
| `AlwaysFailQualityChecker` | 始终返回 `false` | 测试降级路径 |
|
| `AlwaysFailQualityChecker` | 始终返回 `false` | 测试降级路径 |
|
||||||
|
|
||||||
> :bulb: **可扩展性**:`QualityChecker` 是一个可替换的函数变量,未来可接入 LLM 视觉模型进行真正的质量评估。
|
> [!tip] 可扩展性:`QualityChecker` 是一个可替换的函数变量,未来可接入 LLM 视觉模型进行真正的质量评估。
|
||||||
|
|
||||||
### 4. FormatAdapter — 格式适配
|
### 4. FormatAdapter — 格式适配
|
||||||
|
|
||||||
@@ -318,7 +338,7 @@ type PipelineInput struct {
|
|||||||
|
|
||||||
## 关联文档
|
## 关联文档
|
||||||
|
|
||||||
- [索引](00-index.md) — 文档导航与架构总览图
|
- [[00-索引]] — 文档导航与架构总览图
|
||||||
- [系统总览](01-system-overview.md) — 分层架构与 Service 层定位
|
- [[01-系统总览]] — 分层架构与 Service 层定位
|
||||||
- [协程池](02-worker-pool.md) — 管线执行的并发控制
|
- [[02-协程池]] — 管线执行的并发控制
|
||||||
- [任务队列](03-task-queue.md) — 管线任务的排队机制
|
- [[03-任务队列]] — 管线任务的排队机制
|
||||||
|
|||||||
+38
-23
@@ -1,18 +1,27 @@
|
|||||||
# 06 — 精灵图处理管线
|
---
|
||||||
|
tags: [image-processing, sprite-sheet, gif, computer-vision, go, algorithm]
|
||||||
|
create time: 2026-06-03 10:25
|
||||||
|
---
|
||||||
|
|
||||||
> **一句话概括**:自动精灵图处理管线 — 背景移除 → 投影检测 → 切割 → 对齐 → GIF 预览,将一张 AI 生成的 Sprite Sheet 无缝转化为可用的逐帧动画资源。
|
# 06. 精灵图处理管线
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
自动精灵图处理管线 — 背景移除 → 投影检测 → 切割 → 对齐 → GIF 预览,将一张 AI 生成的 Sprite Sheet 无缝转化为可用的逐帧动画资源。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 正文
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
flowchart LR
|
||||||
A["🖼️ Input PNG<br/>Sprite Sheet"] --> B["🪄 Background<br/>Removal"]
|
A["Input PNG"] --> B["Background Removal"]
|
||||||
B --> C["📊 Gap<br/>Detection"]
|
B --> C["Gap Detection"]
|
||||||
C --> D["✂️ Tile<br/>Extract"]
|
C --> D["Tile Extract"]
|
||||||
D --> E["🔍 Filter<br/>MinFill"]
|
D --> E["Filter MinFill"]
|
||||||
E --> F["📐 Trim<br/>Alpha"]
|
E --> F["Trim Alpha"]
|
||||||
F --> G["🎯 Align<br/>padToLargest"]
|
F --> G["Align padToLargest"]
|
||||||
G --> H["🎬 GIF<br/>Preview"]
|
G --> H["GIF Preview"]
|
||||||
|
|
||||||
style A fill:#e3f2fd,stroke:#1976d2
|
style A fill:#e3f2fd,stroke:#1976d2
|
||||||
style B fill:#fff3e0,stroke:#f57c00
|
style B fill:#fff3e0,stroke:#f57c00
|
||||||
@@ -26,7 +35,7 @@ flowchart LR
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📋 处理管线总览
|
## 处理管线总览
|
||||||
|
|
||||||
AI 生成的 Sprite Sheet 通常包含白色/绿色背景、不均匀间距和尺寸差异。Gen2D 的精灵图处理管线自动完成从"原始 PNG"到"可用动画帧"的全部转换工作,无需用户手动操作。
|
AI 生成的 Sprite Sheet 通常包含白色/绿色背景、不均匀间距和尺寸差异。Gen2D 的精灵图处理管线自动完成从"原始 PNG"到"可用动画帧"的全部转换工作,无需用户手动操作。
|
||||||
|
|
||||||
@@ -42,7 +51,7 @@ AI 生成的 Sprite Sheet 通常包含白色/绿色背景、不均匀间距和
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🪄 步骤 1:背景移除
|
## 步骤 1:背景移除
|
||||||
|
|
||||||
AI 生成的图片通常带有纯色背景,管线支持两种模式:
|
AI 生成的图片通常带有纯色背景,管线支持两种模式:
|
||||||
|
|
||||||
@@ -69,11 +78,12 @@ if gDominance > tolerance*255 → 透明化
|
|||||||
- **GreenTolerance** 默认 0.2,控制绿色检测灵敏度
|
- **GreenTolerance** 默认 0.2,控制绿色检测灵敏度
|
||||||
- 适用于绿色背景的 AI 生成图
|
- 适用于绿色背景的 AI 生成图
|
||||||
|
|
||||||
> 💡 **设计选择**:两种模式互斥,`Process()` 根据 `opts.WhiteBg` / `opts.GreenScreen` 自动选择。
|
> [!tip] 设计选择
|
||||||
|
> 两种模式互斥,`Process()` 根据 `opts.WhiteBg` / `opts.GreenScreen` 自动选择。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📊 步骤 2:切割策略
|
## 步骤 2:切割策略
|
||||||
|
|
||||||
管线提供两种切割方式,根据配置自动切换:
|
管线提供两种切割方式,根据配置自动切换:
|
||||||
|
|
||||||
@@ -90,6 +100,10 @@ if gDominance > tolerance*255 → 透明化
|
|||||||
- 防止突出物(武器/尾巴)被其他行稀释
|
- 防止突出物(武器/尾巴)被其他行稀释
|
||||||
- 每行段获得独立的列边界,互不干扰
|
- 每行段获得独立的列边界,互不干扰
|
||||||
|
|
||||||
|
> [!question] 为什么不用简单的阈值分割?
|
||||||
|
>
|
||||||
|
> 精灵图中的角色往往有复杂轮廓——比如挥舞的剑可能横跨多个帧的位置。如果仅用固定阈值,剑的连续像素会让算法误判为一帧。**按行段独立分析**的思路是把二维问题拆解为多个一维子问题,每个子问题只关心当前行段的内容,从而避免跨行干扰。
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TB
|
flowchart TB
|
||||||
A["输入图像"] --> B["全局行投影<br/>检测行间隙"]
|
A["输入图像"] --> B["全局行投影<br/>检测行间隙"]
|
||||||
@@ -124,7 +138,7 @@ flowchart TB
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔍 步骤 3:过滤 — MinFillRatio
|
## 步骤 3:过滤 — MinFillRatio
|
||||||
|
|
||||||
切割后的每个 tile 都计算填充率:
|
切割后的每个 tile 都计算填充率:
|
||||||
|
|
||||||
@@ -138,7 +152,7 @@ fillRatio = 非透明像素数 / 总像素数
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📐 步骤 4:裁剪 — trimAlpha
|
## 步骤 4:裁剪 — trimAlpha
|
||||||
|
|
||||||
对每个 tile 执行透明边框裁剪:
|
对每个 tile 执行透明边框裁剪:
|
||||||
|
|
||||||
@@ -148,7 +162,7 @@ fillRatio = 非透明像素数 / 总像素数
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🎯 步骤 5:对齐 — padToLargest
|
## 步骤 5:对齐 — padToLargest
|
||||||
|
|
||||||
动画播放时,如果每帧尺寸不同且内容未对齐,会导致角色"抖动"。
|
动画播放时,如果每帧尺寸不同且内容未对齐,会导致角色"抖动"。
|
||||||
|
|
||||||
@@ -169,7 +183,7 @@ canvasH = maxH * 110% // 最大帧高度 + 10% padding
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🎬 GIF Maker
|
## GIF Maker
|
||||||
|
|
||||||
`gifmaker.Encode()` 将处理后的帧序列编码为动画 GIF:
|
`gifmaker.Encode()` 将处理后的帧序列编码为动画 GIF:
|
||||||
|
|
||||||
@@ -186,11 +200,12 @@ anim.Disposal = append(anim.Disposal, gif.DisposalBackground)
|
|||||||
anim.BackgroundIndex = 0 // 透明色
|
anim.BackgroundIndex = 0 // 透明色
|
||||||
```
|
```
|
||||||
|
|
||||||
> ⚠️ **DisposalBackground 的重要性**:如果不设置此选项,GIF 播放器会在前一帧基础上叠加新帧,产生"残影"效果。
|
> [!warning] DisposalBackground 的重要性
|
||||||
|
> 如果不设置此选项,GIF 播放器会在前一帧基础上叠加新帧,产生"残影"效果。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📦 Options 配置速查
|
## Options 配置速查
|
||||||
|
|
||||||
| 参数 | 类型 | 默认值 | 说明 |
|
| 参数 | 类型 | 默认值 | 说明 |
|
||||||
|------|------|--------|------|
|
|------|------|--------|------|
|
||||||
@@ -207,8 +222,8 @@ anim.BackgroundIndex = 0 // 透明色
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔗 关联文档
|
## 关联文档
|
||||||
|
|
||||||
- [← 返回索引](00-index.md)
|
- [[00-索引]] — 文档导航与架构总览图
|
||||||
- [05 — 生成管线](05-generation-pipeline.md) — 管线中 SplitSprite 节点的调用方
|
- [[05-生成管线]] — 管线中 SplitSprite 节点的调用方
|
||||||
- [08 — SSE 实时推送](08-sse-push.md) — 处理进度的实时推送
|
- [[08-SSE实时推送]] — 处理进度的实时推送
|
||||||
|
|||||||
+32
-39
@@ -1,28 +1,19 @@
|
|||||||
# 07 — 可观测性
|
---
|
||||||
|
tags: [observability, prometheus, grafana, metrics, alerting, monitoring]
|
||||||
|
create time: 2026-06-03 10:30
|
||||||
|
---
|
||||||
|
|
||||||
> **一句话概括**:35 个 Prometheus 指标 + 3 个 Grafana 仪表盘 + 10 条告警规则,覆盖全栈,让系统运行状态一目了然。
|
# 07. 可观测性
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
35 个 Prometheus 指标 + 3 个 Grafana 仪表盘 + 10 条告警规则,覆盖全栈,让系统运行状态一目了然。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
```mermaid
|
## 正文
|
||||||
flowchart LR
|
|
||||||
A["🌐 Request"] --> B["📊 Metrics<br/>Middleware"]
|
|
||||||
B --> C["⚙️ Application<br/>Logic"]
|
|
||||||
C --> D["📈 Prometheus<br/>Scrape"]
|
|
||||||
D --> E["📉 Grafana<br/>Dashboard"]
|
|
||||||
D --> F["🚨 AlertManager<br/>Notify"]
|
|
||||||
|
|
||||||
style A fill:#e3f2fd,stroke:#1976d2
|
## 指标体系总览
|
||||||
style B fill:#fff3e0,stroke:#f57c00
|
|
||||||
style C fill:#e8f5e9,stroke:#388e3c
|
|
||||||
style D fill:#fce4ec,stroke:#c62828
|
|
||||||
style E fill:#e8eaf6,stroke:#303f9f
|
|
||||||
style F fill:#ffebee,stroke:#b71c1c
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📐 指标体系总览
|
|
||||||
|
|
||||||
Gen2D 遵循 Prometheus 命名最佳实践,所有指标使用 `gen2d_` 前缀,共 **35 个指标**,分为 **6 大组**:
|
Gen2D 遵循 Prometheus 命名最佳实践,所有指标使用 `gen2d_` 前缀,共 **35 个指标**,分为 **6 大组**:
|
||||||
|
|
||||||
@@ -37,9 +28,9 @@ Gen2D 遵循 Prometheus 命名最佳实践,所有指标使用 `gen2d_` 前缀
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📊 五大指标组详解
|
## 五大指标组详解
|
||||||
|
|
||||||
### 1️⃣ HTTP 层指标
|
### HTTP 层指标
|
||||||
|
|
||||||
Gin 中间件自动采集,**零业务代码侵入**。
|
Gin 中间件自动采集,**零业务代码侵入**。
|
||||||
|
|
||||||
@@ -51,9 +42,10 @@ Gin 中间件自动采集,**零业务代码侵入**。
|
|||||||
| `gen2d_http_response_size_bytes` | Histogram | method, path | 响应体大小 |
|
| `gen2d_http_response_size_bytes` | Histogram | method, path | 响应体大小 |
|
||||||
| `gen2d_http_requests_in_flight` | Gauge | — | 当前并发请求数 |
|
| `gen2d_http_requests_in_flight` | Gauge | — | 当前并发请求数 |
|
||||||
|
|
||||||
> 💡 **FullPath() 的关键作用**:使用路由模板 `/api/v1/tasks/:taskId` 而非实际路径 `/api/v1/tasks/abc123`,避免高基数标签导致 Prometheus 内存爆炸。
|
> [!tip] FullPath() 的关键作用
|
||||||
|
> 使用路由模板 `/api/v1/tasks/:taskId` 而非实际路径 `/api/v1/tasks/abc123`,避免高基数标签导致 Prometheus 内存爆炸。
|
||||||
|
|
||||||
### 2️⃣ 限流层指标
|
### 限流层指标
|
||||||
|
|
||||||
| 指标名 | 类型 | 标签 | 说明 |
|
| 指标名 | 类型 | 标签 | 说明 |
|
||||||
|--------|------|------|------|
|
|--------|------|------|------|
|
||||||
@@ -63,7 +55,7 @@ Gin 中间件自动采集,**零业务代码侵入**。
|
|||||||
- `scope`:`user` / `global`
|
- `scope`:`user` / `global`
|
||||||
- `result`:`allowed` / `denied`
|
- `result`:`allowed` / `denied`
|
||||||
|
|
||||||
### 3️⃣ 任务队列层指标
|
### 任务队列层指标
|
||||||
|
|
||||||
| 指标名 | 类型 | 标签 | 说明 |
|
| 指标名 | 类型 | 标签 | 说明 |
|
||||||
|--------|------|------|------|
|
|--------|------|------|------|
|
||||||
@@ -75,7 +67,7 @@ Gin 中间件自动采集,**零业务代码侵入**。
|
|||||||
|
|
||||||
- `driver`:`memory` / `rabbitmq`
|
- `driver`:`memory` / `rabbitmq`
|
||||||
|
|
||||||
### 4️⃣ 协程池层指标
|
### 协程池层指标
|
||||||
|
|
||||||
| 指标名 | 类型 | 标签 | 说明 |
|
| 指标名 | 类型 | 标签 | 说明 |
|
||||||
|--------|------|------|------|
|
|--------|------|------|------|
|
||||||
@@ -86,7 +78,7 @@ Gin 中间件自动采集,**零业务代码侵入**。
|
|||||||
| `gen2d_pool_rejected_total` | Counter | — | 被拒绝的任务 |
|
| `gen2d_pool_rejected_total` | Counter | — | 被拒绝的任务 |
|
||||||
| `gen2d_pool_task_duration_seconds` | Histogram | — | 任务执行耗时 |
|
| `gen2d_pool_task_duration_seconds` | Histogram | — | 任务执行耗时 |
|
||||||
|
|
||||||
### 5️⃣ Pipeline 业务层指标
|
### Pipeline 业务层指标
|
||||||
|
|
||||||
| 指标名 | 类型 | 标签 | 说明 |
|
| 指标名 | 类型 | 标签 | 说明 |
|
||||||
|--------|------|------|------|
|
|--------|------|------|------|
|
||||||
@@ -96,9 +88,10 @@ Gin 中间件自动采集,**零业务代码侵入**。
|
|||||||
| `gen2d_pipeline_retries_total` | CounterVec | stage | 各阶段重试次数 |
|
| `gen2d_pipeline_retries_total` | CounterVec | stage | 各阶段重试次数 |
|
||||||
| `gen2d_pipeline_tasks_active` | Gauge | — | 当前执行中的 Pipeline 数 |
|
| `gen2d_pipeline_tasks_active` | Gauge | — | 当前执行中的 Pipeline 数 |
|
||||||
|
|
||||||
> 🔍 **stage_duration 定位瓶颈**:通过 `stage` 标签(如 `asset_generator`、`quality_check`)可以精确定位哪个阶段是性能瓶颈。
|
> [!tip] stage_duration 定位瓶颈
|
||||||
|
> 通过 `stage` 标签(如 `asset_generator`、`quality_check`)可以精确定位哪个阶段是性能瓶颈。
|
||||||
|
|
||||||
### 6️⃣ 基础设施层指标
|
### 基础设施层指标
|
||||||
|
|
||||||
| 指标名 | 类型 | 标签 | 说明 |
|
| 指标名 | 类型 | 标签 | 说明 |
|
||||||
|--------|------|------|------|
|
|--------|------|------|------|
|
||||||
@@ -110,7 +103,7 @@ Gin 中间件自动采集,**零业务代码侵入**。
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔧 中间件集成
|
## 中间件集成
|
||||||
|
|
||||||
### Metrics 中间件工作流程
|
### Metrics 中间件工作流程
|
||||||
|
|
||||||
@@ -136,7 +129,7 @@ func Metrics() gin.HandlerFunc {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📉 Grafana 仪表盘
|
## Grafana 仪表盘
|
||||||
|
|
||||||
| 仪表盘 | 用途 | 关键面板 |
|
| 仪表盘 | 用途 | 关键面板 |
|
||||||
|--------|------|---------|
|
|--------|------|---------|
|
||||||
@@ -146,7 +139,7 @@ func Metrics() gin.HandlerFunc {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚨 告警规则
|
## 告警规则
|
||||||
|
|
||||||
共 **10 条告警规则**,覆盖限流、队列、协程池、管线和基础设施:
|
共 **10 条告警规则**,覆盖限流、队列、协程池、管线和基础设施:
|
||||||
|
|
||||||
@@ -163,11 +156,12 @@ func Metrics() gin.HandlerFunc {
|
|||||||
| `HighErrorRate` | 🔴 | 5xx 错误率 > 5%(持续 5m) | 服务异常 |
|
| `HighErrorRate` | 🔴 | 5xx 错误率 > 5%(持续 5m) | 服务异常 |
|
||||||
| `HighLatency` | ⚠️ | P95 延迟 > 5s(持续 5m) | 影响用户体验 |
|
| `HighLatency` | ⚠️ | P95 延迟 > 5s(持续 5m) | 影响用户体验 |
|
||||||
|
|
||||||
> 🛡️ **告警级别说明**:🔴 Critical 表示需要立即处理,⚠️ Warning 表示需要关注但不紧急。
|
> [!note] 告警级别说明
|
||||||
|
> Critical 表示需要立即处理,Warning 表示需要关注但不紧急。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📦 基础设施指标
|
## 基础设施指标
|
||||||
|
|
||||||
除业务指标外,Gen2D 还监控外部依赖的健康状态:
|
除业务指标外,Gen2D 还监控外部依赖的健康状态:
|
||||||
|
|
||||||
@@ -194,9 +188,8 @@ flowchart LR
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔗 关联文档
|
## 关联文档
|
||||||
|
|
||||||
- [← 返回索引](00-index.md)
|
- [[10-中间件链]] — Metrics 中间件的挂载位置
|
||||||
- [10 — 中间件链](10-middleware-chain.md) — Metrics 中间件的挂载位置
|
- [[09-限流]] — 限流指标的采集方式
|
||||||
- [09 — 限流](09-rate-limiting.md) — 限流指标的采集方式
|
- [[14-部署架构]] — Prometheus + Grafana 的部署配置
|
||||||
- [14 — 部署架构](14-deployment.md) — Prometheus + Grafana 的部署配置
|
|
||||||
|
|||||||
+26
-18
@@ -1,16 +1,25 @@
|
|||||||
# 08 — SSE 实时推送
|
---
|
||||||
|
tags: [sse, event-stream, pub-sub, real-time, websockets-alternative, go]
|
||||||
|
create time: 2026-06-03 10:35
|
||||||
|
---
|
||||||
|
|
||||||
> **一句话概括**:内存 EventBus 发布/订阅,SSE 推送管线进度到浏览器,让用户实时看到生成过程。
|
# 08. SSE 实时推送
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
内存 EventBus 发布/订阅,SSE 推送管线进度到浏览器,让用户实时看到生成过程。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 正文
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
flowchart LR
|
||||||
P["⚙️ Pipeline<br/>Callback"] -->|"Publish"| EB["📡 EventBus<br/>Broker"]
|
P["Pipeline Callback"] -->|"Publish"| EB["EventBus Broker"]
|
||||||
EB -->|"Subscribe<br/>taskID"| H1["🌐 SSE Handler<br/>/tasks/:id/stream"]
|
EB -->|"Subscribe taskID"| H1["SSE Handler /tasks/:id/stream"]
|
||||||
EB -->|"SubscribeAll<br/>global"| H2["🌐 SSE Handler<br/>/projects/:id/stream"]
|
EB -->|"SubscribeAll global"| H2["SSE Handler /projects/:id/stream"]
|
||||||
H1 -->|"text/event-stream"| B1["🖥️ Browser<br/>EventSource"]
|
H1 -->|"text/event-stream"| B1["Browser EventSource"]
|
||||||
H2 -->|"text/event-stream"| B2["🖥️ Browser<br/>EventSource"]
|
H2 -->|"text/event-stream"| B2["Browser EventSource"]
|
||||||
|
|
||||||
style P fill:#e8f5e9,stroke:#388e3c
|
style P fill:#e8f5e9,stroke:#388e3c
|
||||||
style EB fill:#fff3e0,stroke:#f57c00
|
style EB fill:#fff3e0,stroke:#f57c00
|
||||||
@@ -22,7 +31,7 @@ flowchart LR
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📡 EventBus 架构
|
## EventBus 架构
|
||||||
|
|
||||||
EventBus 是 Gen2D 的内存事件总线,负责在 Pipeline 执行过程中发布进度事件,并由 SSE Handler 订阅推送给客户端。
|
EventBus 是 Gen2D 的内存事件总线,负责在 Pipeline 执行过程中发布进度事件,并由 SSE Handler 订阅推送给客户端。
|
||||||
|
|
||||||
@@ -57,7 +66,7 @@ type TaskEvent struct {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔔 订阅模式
|
## 订阅模式
|
||||||
|
|
||||||
### Subscribe(taskID) — 任务级订阅
|
### Subscribe(taskID) — 任务级订阅
|
||||||
|
|
||||||
@@ -93,7 +102,7 @@ func (b *Broker) SubscribeAll() <-chan TaskEvent {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📤 Publish — 扇出分发
|
## Publish — 扇出分发
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func (b *Broker) Publish(taskID string, event TaskEvent) {
|
func (b *Broker) Publish(taskID string, event TaskEvent) {
|
||||||
@@ -119,7 +128,7 @@ func (b *Broker) Publish(taskID string, event TaskEvent) {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🌐 SSE Handler
|
## SSE Handler
|
||||||
|
|
||||||
### Stream — 任务级流
|
### Stream — 任务级流
|
||||||
|
|
||||||
@@ -175,7 +184,7 @@ GET /api/v1/projects/:projectId/stream
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📊 数据流全景
|
## 数据流全景
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
sequenceDiagram
|
sequenceDiagram
|
||||||
@@ -199,7 +208,7 @@ sequenceDiagram
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🛡️ 容错设计
|
## 容错设计
|
||||||
|
|
||||||
| 场景 | 处理方式 |
|
| 场景 | 处理方式 |
|
||||||
|------|---------|
|
|------|---------|
|
||||||
@@ -211,9 +220,8 @@ sequenceDiagram
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔗 关联文档
|
## 关联文档
|
||||||
|
|
||||||
- [← 返回索引](00-index.md)
|
- [[05-生成管线]] — Pipeline 中的进度回调
|
||||||
- [05 — 生成管线](05-generation-pipeline.md) — Pipeline 中的进度回调
|
- [[07-可观测性]] — SSE 连接的监控
|
||||||
- [07 — 可观测性](07-observability.md) — SSE 连接的监控
|
- [[10-中间件链]] — SSE 端点的中间件配置
|
||||||
- [10 — 中间件链](10-middleware-chain.md) — SSE 端点的中间件配置
|
|
||||||
|
|||||||
+35
-17
@@ -1,16 +1,25 @@
|
|||||||
# 09 — 限流
|
---
|
||||||
|
tags: [rate-limiting, redis, lua, token-bucket, distributed-system, go]
|
||||||
|
create time: 2026-06-03 10:40
|
||||||
|
---
|
||||||
|
|
||||||
> **一句话概括**:Redis Lua 原子令牌桶 + 双层限流 + Fail-Open 降级,保护系统免受过载。
|
# 09. 限流
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
Redis Lua 原子令牌桶 + 双层限流 + Fail-Open 降级,保护系统免受过载。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## 正文
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
flowchart LR
|
||||||
A["🌐 Request"] --> B["🌍 Global<br/>Limiter"]
|
A["Request"] --> B["Global Limiter"]
|
||||||
B -->|pass| C["👤 User<br/>Limiter"]
|
B -->|pass| C["User Limiter"]
|
||||||
B -->|deny| F["❌ 429"]
|
B -->|deny| F["429"]
|
||||||
B -->|redis-fail| C
|
B -->|redis-fail| C
|
||||||
C -->|pass| D["✅ Handler"]
|
C -->|pass| D["Handler"]
|
||||||
C -->|deny| F
|
C -->|deny| F
|
||||||
C -->|redis-fail| D
|
C -->|redis-fail| D
|
||||||
|
|
||||||
@@ -23,7 +32,7 @@ flowchart LR
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ⚙️ 令牌桶算法
|
## 令牌桶算法
|
||||||
|
|
||||||
Gen2D 使用 **Redis + Lua 脚本** 实现分布式令牌桶限流,保证原子性和一致性。
|
Gen2D 使用 **Redis + Lua 脚本** 实现分布式令牌桶限流,保证原子性和一致性。
|
||||||
|
|
||||||
@@ -75,11 +84,12 @@ return {allowed, tokens, retry_after}
|
|||||||
- 用完后不补充(`rate = 0` 时跳过 refill)
|
- 用完后不补充(`rate = 0` 时跳过 refill)
|
||||||
- 等待 key 过期后重置(`Expiration` 控制窗口大小)
|
- 等待 key 过期后重置(`Expiration` 控制窗口大小)
|
||||||
|
|
||||||
> 💡 **适用场景**:24 小时维度的配额控制,如"每天 30 次提示词优化"。
|
> [!tip] 适用场景
|
||||||
|
> 24 小时维度的配额控制,如"每天 30 次提示词优化"。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔀 双层限流配置
|
## 双层限流配置
|
||||||
|
|
||||||
Gen2D 对核心接口实施**全局限流 + 用户限流**双重保护:
|
Gen2D 对核心接口实施**全局限流 + 用户限流**双重保护:
|
||||||
|
|
||||||
@@ -126,7 +136,7 @@ type Config struct {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🛡️ Fail-Open 降级
|
## Fail-Open 降级
|
||||||
|
|
||||||
当 Redis 不可用时限流器自动降级为 **Fail-Open** 模式:
|
当 Redis 不可用时限流器自动降级为 **Fail-Open** 模式:
|
||||||
|
|
||||||
@@ -148,11 +158,20 @@ func (l *TokenBucketLimiter) Allow(ctx context.Context, key string) (bool, int,
|
|||||||
| **Fail-Open** ✅ | 保证可用性,用户体验不受影响 | 可能短暂失去限流保护 |
|
| **Fail-Open** ✅ | 保证可用性,用户体验不受影响 | 可能短暂失去限流保护 |
|
||||||
| Fail-Close | 严格限流保护 | Redis 故障导致全站不可用 |
|
| Fail-Close | 严格限流保护 | Redis 故障导致全站不可用 |
|
||||||
|
|
||||||
> 🛡️ **选择 Fail-Open**:在"偶尔超限"和"完全不可用"之间,优先保证服务可用性。
|
> [!question] 为什么选择 Fail-Open 而不是 Fail-Close?
|
||||||
|
>
|
||||||
|
> 这是 **「可用性 vs 安全性」** 的经典抉择。在 Gen2D 的场景中:
|
||||||
|
> - 限流失效的代价:短时间内有人可能超出配额(几分钟到几小时)
|
||||||
|
> - 限流强固化的代价:**所有用户都无法使用服务**
|
||||||
|
>
|
||||||
|
> 显然,前者是可以接受的风险——超出配额的用户可以后续通过账单追缴;而后者意味着业务完全停摆。这种「宁可放宽、不可收紧」的设计哲学在基础设施层非常重要。
|
||||||
|
|
||||||
|
> [!note] 选择 Fail-Open
|
||||||
|
> 在"偶尔超限"和"完全不可用"之间,优先保证服务可用性。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📡 中间件响应
|
## 中间件响应
|
||||||
|
|
||||||
限流中间件返回标准化的 HTTP 响应:
|
限流中间件返回标准化的 HTTP 响应:
|
||||||
|
|
||||||
@@ -183,7 +202,7 @@ X-RateLimit-Remaining: 0
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📊 指标采集
|
## 指标采集
|
||||||
|
|
||||||
限流中间件自动采集 Prometheus 指标:
|
限流中间件自动采集 Prometheus 指标:
|
||||||
|
|
||||||
@@ -200,8 +219,7 @@ metrics.RateLimitRemainingTokens.WithLabelValues(scope, endpoint).Set(float64(re
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔗 关联文档
|
## 关联文档
|
||||||
|
|
||||||
- [← 返回索引](00-index.md)
|
- [[10-中间件链]] — 限流中间件在链中的位置
|
||||||
- [10 — 中间件链](10-middleware-chain.md) — 限流中间件在链中的位置
|
- [[07-可观测性]] — 限流指标和告警规则
|
||||||
- [07 — 可观测性](07-observability.md) — 限流指标和告警规则
|
|
||||||
|
|||||||
+45
-43
@@ -1,23 +1,32 @@
|
|||||||
# 10 — 中间件链
|
|
||||||
|
|
||||||
> **一句话概括**:Logger → Recovery → Metrics → Auth → RateLimit → Handler,洋葱模型,层层守护请求处理。
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
tags: [middleware, gin, onion-pattern, auth, logging, recovery]
|
||||||
|
create time: 2026-06-03 10:45
|
||||||
|
---
|
||||||
|
|
||||||
|
# 10. 中间件链
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
Logger → Recovery → Metrics → Auth → RateLimit → Handler,洋葱模型,层层守护请求处理。
|
||||||
|
|
||||||
|
## 正文
|
||||||
|
|
||||||
|
### 请求处理链路
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
flowchart LR
|
||||||
A["🌐 Request"] --> B["📝 Logger"]
|
A["Request"] --> B["Logger"]
|
||||||
B --> C["🛡️ Recovery"]
|
B --> C["Recovery"]
|
||||||
C --> D["📊 Metrics"]
|
C --> D["Metrics"]
|
||||||
D --> E["🔐 Auth"]
|
D --> E["Auth"]
|
||||||
E --> F["🚦 RateLimit"]
|
E --> F["RateLimit"]
|
||||||
F --> G["⚙️ Handler"]
|
F --> G["Handler"]
|
||||||
G --> F
|
G --> F
|
||||||
F --> E
|
F --> E
|
||||||
E --> D
|
E --> D
|
||||||
D --> C
|
D --> C
|
||||||
C --> B
|
C --> B
|
||||||
B --> H["📡 Response"]
|
B --> H["Response"]
|
||||||
|
|
||||||
style A fill:#e3f2fd,stroke:#1976d2
|
style A fill:#e3f2fd,stroke:#1976d2
|
||||||
style B fill:#e8f5e9,stroke:#388e3c
|
style B fill:#e8f5e9,stroke:#388e3c
|
||||||
@@ -29,24 +38,17 @@ flowchart LR
|
|||||||
style H fill:#e8eaf6,stroke:#303f9f
|
style H fill:#e8eaf6,stroke:#303f9f
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
### 洋葱模型
|
||||||
|
|
||||||
## 🧅 洋葱模型
|
|
||||||
|
|
||||||
Gin 的中间件采用**洋葱模型**:请求从外到内穿过各中间件,响应从内到外返回。每个中间件可以在 `c.Next()` 前后执行逻辑。
|
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 — 请求日志
|
### Logger — 请求日志
|
||||||
|
|
||||||
**职责**:为每个请求生成唯一 ID,记录请求详情。
|
**职责**:为每个请求生成唯一 ID,记录请求详情。
|
||||||
|
|
||||||
### 核心逻辑
|
#### 核心逻辑
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func Logger() gin.HandlerFunc {
|
func Logger() gin.HandlerFunc {
|
||||||
@@ -63,7 +65,7 @@ func Logger() gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### RequestID 生成
|
#### RequestID 生成
|
||||||
|
|
||||||
```go
|
```go
|
||||||
func generateRequestID() string {
|
func generateRequestID() string {
|
||||||
@@ -80,7 +82,7 @@ func generateRequestID() string {
|
|||||||
| 时间戳(毫秒) | 保证时间有序性 |
|
| 时间戳(毫秒) | 保证时间有序性 |
|
||||||
| 8 字节随机 hex | 保证唯一性 |
|
| 8 字节随机 hex | 保证唯一性 |
|
||||||
|
|
||||||
### 日志级别映射
|
#### 日志级别映射
|
||||||
|
|
||||||
| HTTP 状态码 | 日志级别 | 含义 |
|
| HTTP 状态码 | 日志级别 | 含义 |
|
||||||
|:-----------:|:-------:|------|
|
|:-----------:|:-------:|------|
|
||||||
@@ -90,7 +92,7 @@ func generateRequestID() string {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🛡️ Recovery — Panic 恢复
|
### Recovery — Panic 恢复
|
||||||
|
|
||||||
**职责**:捕获未处理的 panic,防止服务崩溃。
|
**职责**:捕获未处理的 panic,防止服务崩溃。
|
||||||
|
|
||||||
@@ -124,7 +126,7 @@ func Recovery() gin.HandlerFunc {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📊 Metrics — 指标采集
|
### Metrics — 指标采集
|
||||||
|
|
||||||
**职责**:自动采集 HTTP 请求的性能指标。
|
**职责**:自动采集 HTTP 请求的性能指标。
|
||||||
|
|
||||||
@@ -152,11 +154,12 @@ func Metrics() gin.HandlerFunc {
|
|||||||
| ResponseSize | `c.Next()` 之后 | Writer 此时已写入 |
|
| ResponseSize | `c.Next()` 之后 | Writer 此时已写入 |
|
||||||
| Duration | `c.Next()` 之后 | 需要计算总耗时 |
|
| Duration | `c.Next()` 之后 | 需要计算总耗时 |
|
||||||
|
|
||||||
> 💡 **FullPath() 的关键作用**:使用路由模板 `/api/v1/tasks/:taskId` 而非实际路径,避免高基数标签导致 Prometheus 内存爆炸。
|
> [!tip] FullPath() 的关键作用
|
||||||
|
> 使用路由模板 `/api/v1/tasks/:taskId` 而非实际路径,避免高基数标签导致 Prometheus 内存爆炸。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔐 Auth — JWT 认证
|
### Auth — JWT 认证
|
||||||
|
|
||||||
**职责**:验证 Bearer token,提取用户身份。
|
**职责**:验证 Bearer token,提取用户身份。
|
||||||
|
|
||||||
@@ -188,13 +191,13 @@ func AuthMiddleware(jwtSecret string) gin.HandlerFunc {
|
|||||||
| 场景 | HTTP 状态码 | 消息 |
|
| 场景 | HTTP 状态码 | 消息 |
|
||||||
|------|:-----------:|------|
|
|------|:-----------:|------|
|
||||||
| 无 token | 401 | 未提供认证令牌 |
|
| 无 token | 401 | 未提供认证令牌 |
|
||||||
| 格式错误 | 401 | 认证格式错误,需为 Bearer \<token\> |
|
| 格式错误 | 401 | 认证格式错误,需为 Bearer <token> |
|
||||||
| token 无效 | 401 | 令牌无效或已过期 |
|
| token 无效 | 401 | 令牌无效或已过期 |
|
||||||
| 解析失败 | 401 | 令牌解析失败 |
|
| 解析失败 | 401 | 令牌解析失败 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚦 RateLimit — 限流
|
### RateLimit — 限流
|
||||||
|
|
||||||
**职责**:按路由配置执行双层限流(全局 + 用户)。
|
**职责**:按路由配置执行双层限流(全局 + 用户)。
|
||||||
|
|
||||||
@@ -207,13 +210,13 @@ v1Auth.POST("/generate",
|
|||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
详见 [09 — 限流](09-rate-limiting.md)。
|
详见 [[09-限流]]。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔗 中间件挂载
|
### 中间件挂载
|
||||||
|
|
||||||
### 全局链(所有请求)
|
#### 全局链(所有请求)
|
||||||
|
|
||||||
```go
|
```go
|
||||||
r := gin.New()
|
r := gin.New()
|
||||||
@@ -222,14 +225,14 @@ r.Use(mildware.Recovery()) // 2. Panic 恢复
|
|||||||
r.Use(mildware.Metrics()) // 3. 指标采集
|
r.Use(mildware.Metrics()) // 3. 指标采集
|
||||||
```
|
```
|
||||||
|
|
||||||
### 路由组级链(需认证)
|
#### 路由组级链(需认证)
|
||||||
|
|
||||||
```go
|
```go
|
||||||
v1Auth := r.Group("/api/v1")
|
v1Auth := r.Group("/api/v1")
|
||||||
v1Auth.Use(mildware.AuthMiddleware(cfg.JWT.Secret)) // 4. JWT 认证
|
v1Auth.Use(mildware.AuthMiddleware(cfg.JWT.Secret)) // 4. JWT 认证
|
||||||
```
|
```
|
||||||
|
|
||||||
### 端点级链(需限流)
|
#### 端点级链(需限流)
|
||||||
|
|
||||||
```go
|
```go
|
||||||
v1Auth.POST("/generate",
|
v1Auth.POST("/generate",
|
||||||
@@ -241,7 +244,7 @@ v1Auth.POST("/generate",
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📋 端点链示例:/api/v1/generate
|
### 端点链示例:/api/v1/generate
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TB
|
flowchart TB
|
||||||
@@ -255,7 +258,7 @@ flowchart TB
|
|||||||
H --> I["Metrics<br/>记录 duration, respSize"]
|
H --> I["Metrics<br/>记录 duration, respSize"]
|
||||||
I --> J["Recovery<br/>检查是否 panic"]
|
I --> J["Recovery<br/>检查是否 panic"]
|
||||||
J --> K["Logger<br/>记录请求日志"]
|
J --> K["Logger<br/>记录请求日志"]
|
||||||
K --> L["📡 Response"]
|
K --> L["Response"]
|
||||||
|
|
||||||
style A fill:#e3f2fd,stroke:#1976d2
|
style A fill:#e3f2fd,stroke:#1976d2
|
||||||
style B fill:#e8f5e9,stroke:#388e3c
|
style B fill:#e8f5e9,stroke:#388e3c
|
||||||
@@ -268,7 +271,7 @@ flowchart TB
|
|||||||
style L fill:#e8eaf6,stroke:#303f9f
|
style L fill:#e8eaf6,stroke:#303f9f
|
||||||
```
|
```
|
||||||
|
|
||||||
### 完整请求生命周期
|
#### 完整请求生命周期
|
||||||
|
|
||||||
| 阶段 | 中间件 | 动作 |
|
| 阶段 | 中间件 | 动作 |
|
||||||
|:----:|--------|------|
|
|:----:|--------|------|
|
||||||
@@ -285,7 +288,7 @@ flowchart TB
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📊 中间件职责矩阵
|
### 中间件职责矩阵
|
||||||
|
|
||||||
| 中间件 | 请求进入 | 请求离开 | 异常处理 | 作用范围 |
|
| 中间件 | 请求进入 | 请求离开 | 异常处理 | 作用范围 |
|
||||||
|--------|---------|---------|---------|---------|
|
|--------|---------|---------|---------|---------|
|
||||||
@@ -297,9 +300,8 @@ flowchart TB
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🔗 关联文档
|
## 关联文档
|
||||||
|
|
||||||
- [← 返回索引](00-index.md)
|
- [[07-可观测性]] — Metrics 中间件采集的指标
|
||||||
- [07 — 可观测性](07-observability.md) — Metrics 中间件采集的指标
|
- [[09-限流]] — RateLimit 中间件的详细实现
|
||||||
- [09 — 限流](09-rate-limiting.md) — RateLimit 中间件的详细实现
|
- [[08-SSE实时推送]] — SSE 端点的中间件配置
|
||||||
- [08 — SSE 实时推送](08-sse-push.md) — SSE 端点的中间件配置
|
|
||||||
|
|||||||
@@ -1,34 +1,41 @@
|
|||||||
# 11. Consumer-Producer 桥接模式
|
---
|
||||||
|
tags: [consumer-producer, bridge-pattern, decoupling, go, message-queue]
|
||||||
> **一句话概括**:`Consumer` 结构体桥接 `TaskQueue` 和 `WorkerPool`,实现生产者与消费者的彻底解耦。
|
create time: 2026-06-03 10:50
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 架构总览
|
# 11. Consumer-Producer 桥接模式
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
Consumer 结构体桥接 TaskQueue 和 WorkerPool,实现生产者与消费者的彻底解耦。
|
||||||
|
|
||||||
|
## 正文
|
||||||
|
|
||||||
|
### 架构总览
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
flowchart LR
|
||||||
subgraph Producer["生产者"]
|
subgraph Producer["Producer"]
|
||||||
A[Generate Handler]
|
A["Generate Handler"]
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph Queue["TaskQueue 接口"]
|
subgraph Queue["TaskQueue 接口"]
|
||||||
B((Memory\nQueue))
|
B[("Memory Queue")]
|
||||||
C((RabbitMQ\nQueue))
|
C[("RabbitMQ Queue")]
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph Bridge["Consumer 桥接层"]
|
subgraph Bridge["Consumer 桥接层"]
|
||||||
D{{"Consumer\n(bridge)"}}
|
D{{"Consumer"}}
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph Pool["WorkerPool"]
|
subgraph Pool["WorkerPool"]
|
||||||
E[Worker 1]
|
E["Worker 1"]
|
||||||
F[Worker 2]
|
F["Worker 2"]
|
||||||
G[Worker N]
|
G["Worker N"]
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph Pipeline["业务逻辑"]
|
subgraph Pipeline["业务逻辑"]
|
||||||
H[Eino Pipeline]
|
H["Eino Pipeline"]
|
||||||
end
|
end
|
||||||
|
|
||||||
A -->|"Submit(msg)"| B
|
A -->|"Submit(msg)"| B
|
||||||
@@ -45,9 +52,7 @@ flowchart LR
|
|||||||
style D fill:#f9a825,stroke:#333,color:#000
|
style D fill:#f9a825,stroke:#333,color:#000
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
### 核心结构体
|
||||||
|
|
||||||
## 核心结构体
|
|
||||||
|
|
||||||
`Consumer` 是整个任务调度体系的**桥梁**,它只做一件事:从队列取消息,提交到协程池。
|
`Consumer` 是整个任务调度体系的**桥梁**,它只做一件事:从队列取消息,提交到协程池。
|
||||||
|
|
||||||
@@ -69,14 +74,14 @@ type Consumer struct {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 工作流程
|
### 工作流程
|
||||||
|
|
||||||
### 启动消费循环
|
#### 启动消费循环
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
sequenceDiagram
|
sequenceDiagram
|
||||||
participant Main as main.go
|
participant Main as main.go
|
||||||
participant Consumer
|
participant Consumer as Consumer
|
||||||
participant Queue as TaskQueue
|
participant Queue as TaskQueue
|
||||||
participant Pool as WorkerPool
|
participant Pool as WorkerPool
|
||||||
participant Handler as RunFromTaskMessage
|
participant Handler as RunFromTaskMessage
|
||||||
@@ -102,20 +107,20 @@ sequenceDiagram
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 解耦的三层设计
|
### 解耦的三层设计
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TB
|
flowchart TB
|
||||||
subgraph "第 1 层:消息源"
|
subgraph "消息源"
|
||||||
Q["TaskQueue 接口\n(Memory / RabbitMQ)"]
|
Q["TaskQueue 接口\n(Memory / RabbitMQ)"]
|
||||||
end
|
end
|
||||||
subgraph "第 2 层:桥接"
|
subgraph "桥接"
|
||||||
C["Consumer\n(只关心 消费→提交)"]
|
C["Consumer\n(只关心 消费→提交)"]
|
||||||
end
|
end
|
||||||
subgraph "第 3 层:执行引擎"
|
subgraph "执行引擎"
|
||||||
P["WorkerPool\n(只关心 并发控制)"]
|
P["WorkerPool\n(只关心 并发控制)"]
|
||||||
end
|
end
|
||||||
subgraph "第 4 层:业务逻辑"
|
subgraph "业务逻辑"
|
||||||
H["TaskHandler 回调\n(RunFromTaskMessage)"]
|
H["TaskHandler 回调\n(RunFromTaskMessage)"]
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -133,7 +138,7 @@ flowchart TB
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Handler 层注入
|
### Handler 层注入
|
||||||
|
|
||||||
`Consumer` 不硬编码业务逻辑,而是通过 `TaskHandler` 函数签名由外部注入:
|
`Consumer` 不硬编码业务逻辑,而是通过 `TaskHandler` 函数签名由外部注入:
|
||||||
|
|
||||||
@@ -149,13 +154,13 @@ consumer := worker.NewConsumer(tq, pool, handler.RunFromTaskMessage)
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 信号处理与优雅关闭
|
### 信号处理与优雅关闭
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
sequenceDiagram
|
sequenceDiagram
|
||||||
participant OS as 操作系统
|
participant OS as OS
|
||||||
participant Main as main.go
|
participant Main as main.go
|
||||||
participant Consumer
|
participant Consumer as Consumer
|
||||||
participant Pool as WorkerPool
|
participant Pool as WorkerPool
|
||||||
|
|
||||||
OS->>Main: SIGINT / SIGTERM
|
OS->>Main: SIGINT / SIGTERM
|
||||||
@@ -173,13 +178,9 @@ sequenceDiagram
|
|||||||
2. **再关 WorkerPool** — 等待已提交的任务执行完毕(最多 30 秒)
|
2. **再关 WorkerPool** — 等待已提交的任务执行完毕(最多 30 秒)
|
||||||
3. **最后关闭队列连接** — 释放 RabbitMQ / 内存资源
|
3. **最后关闭队列连接** — 释放 RabbitMQ / 内存资源
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 关联文档
|
## 关联文档
|
||||||
|
|
||||||
| 文档 | 关系 |
|
- [[03-任务队列]] — Consumer 的消息来源
|
||||||
|------|------|
|
- [[02-协程池]] — Consumer 的执行引擎
|
||||||
| [03 - 任务队列](03-task-queue.md) | Consumer 的消息来源 |
|
- [[12-三级降级策略]] — Consumer 不参与降级,降级在 Handler 层
|
||||||
| [02 - 协程池](02-worker-pool.md) | Consumer 的执行引擎 |
|
- [[00-索引]] — 返回文档总览
|
||||||
| [12 - 三级降级策略](12-three-tier-fallback.md) | Consumer 不参与降级,降级在 Handler 层 |
|
|
||||||
| [00 - 索引](00-index.md) | 返回文档总览 |
|
|
||||||
|
|||||||
+37
-27
@@ -1,25 +1,32 @@
|
|||||||
# 12. 三级降级策略
|
---
|
||||||
|
tags: [fallback-pattern, resilience, degradation, error-handling, go]
|
||||||
> **一句话概括**:Generate handler 实现三级降级 — TaskQueue -> WorkerPool -> Legacy FIFO,宁可降级也不拒绝服务。
|
create time: 2026-06-03 10:55
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 降级链总览
|
# 12. 三级降级策略
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
Generate handler 实现三级降级 — TaskQueue -> WorkerPool -> Legacy FIFO,宁可降级也不拒绝服务。
|
||||||
|
|
||||||
|
## 正文
|
||||||
|
|
||||||
|
### 降级链总览
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TD
|
flowchart TD
|
||||||
R["HTTP Request\nPOST /api/v1/generate"] --> A{"TaskQueue\n可用?"}
|
R["HTTP Request\nPOST /api/v1/generate"] --> A{"TaskQueue 可用?"}
|
||||||
|
|
||||||
A -->|"Submit 成功"| S1["200 OK\ntaskId 返回"]
|
A -->|"Submit 成功"| S1["200 OK\ntaskId 返回"]
|
||||||
A -->|"Submit 失败"| B{"WorkerPool\n可用?"}
|
A -->|"Submit 失败"| B{"WorkerPool 可用?"}
|
||||||
|
|
||||||
B -->|"Submit 成功"| S2["200 OK\ntaskId 返回"]
|
B -->|"Submit 成功"| S2["200 OK\ntaskId 返回"]
|
||||||
B -->|"ErrPoolFull"| E2["503 Service\nUnavailable"]
|
B -->|"ErrPoolFull"| E2["503 Service\nUnavailable"]
|
||||||
B -->|"ErrUserLimit"| E3["429 Too Many\nRequests"]
|
B -->|"ErrUserLimit"| E3["429 Too Many Requests"]
|
||||||
B -->|"Pool 不可用"| C{"Legacy FIFO\nQueue 可用?"}
|
B -->|"Pool 不可用"| C{"Legacy FIFO Queue 可用?"}
|
||||||
|
|
||||||
C -->|"Enqueue 成功"| S3["200 OK\ntaskId 返回"]
|
C -->|"Enqueue 成功"| S3["200 OK\ntaskId 返回"]
|
||||||
C -->|"Queue 不可用"| E4["500 Internal\nServer Error"]
|
C -->|"Queue 不可用"| E4["500 Internal Server Error"]
|
||||||
|
|
||||||
style A fill:#4caf50,stroke:#333,color:#fff
|
style A fill:#4caf50,stroke:#333,color:#fff
|
||||||
style B fill:#ff9800,stroke:#333,color:#fff
|
style B fill:#ff9800,stroke:#333,color:#fff
|
||||||
@@ -29,11 +36,9 @@ flowchart TD
|
|||||||
style S3 fill:#8bc34a,stroke:#333,color:#fff
|
style S3 fill:#8bc34a,stroke:#333,color:#fff
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
### 三级详解
|
||||||
|
|
||||||
## 三级详解
|
#### 第一级:TaskQueue(优先路径)
|
||||||
|
|
||||||
### 第一级:TaskQueue(优先路径)
|
|
||||||
|
|
||||||
| 属性 | 说明 |
|
| 属性 | 说明 |
|
||||||
|------|------|
|
|------|------|
|
||||||
@@ -55,7 +60,7 @@ if taskQueue != nil {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 第二级:WorkerPool(有界并发)
|
#### 第二级:WorkerPool(有界并发)
|
||||||
|
|
||||||
| 属性 | 说明 |
|
| 属性 | 说明 |
|
||||||
|------|------|
|
|------|------|
|
||||||
@@ -80,7 +85,7 @@ if workerPool != nil {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### 第三级:Legacy FIFO(最后保底)
|
#### 第三级:Legacy FIFO(最后保底)
|
||||||
|
|
||||||
| 属性 | 说明 |
|
| 属性 | 说明 |
|
||||||
|------|------|
|
|------|------|
|
||||||
@@ -98,7 +103,7 @@ c.JSON(200, taskId)
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## HTTP 状态码映射
|
### HTTP 状态码映射
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
flowchart LR
|
||||||
@@ -133,7 +138,7 @@ flowchart LR
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 为什么需要三级?
|
### 为什么需要三级?
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TB
|
flowchart TB
|
||||||
@@ -157,9 +162,18 @@ flowchart TB
|
|||||||
| WorkerPool | 并发控制 + 背压 | 单机部署,需要限制资源 |
|
| WorkerPool | 并发控制 + 背压 | 单机部署,需要限制资源 |
|
||||||
| Legacy FIFO | 可用性兜底 | 开发/测试环境,或队列组件故障 |
|
| Legacy FIFO | 可用性兜底 | 开发/测试环境,或队列组件故障 |
|
||||||
|
|
||||||
|
> [!tip] 分级降级的核心原则
|
||||||
|
>
|
||||||
|
> **每一级都是前一级功能的超集**。也就是说:
|
||||||
|
> - TaskQueue = 持久化排队 + 重试 + Consumer 消费 + WorkerPool 执行
|
||||||
|
> - WorkerPool = 有界并发 + 直接执行(跳过持久化和消费者)
|
||||||
|
> - Legacy FIFO = 最简串行队列(跳过所有高级特性)
|
||||||
|
>
|
||||||
|
> 这种设计确保降级过程是**渐进的**——功能逐步减少但服务始终可用。类比现实中的「应急灯」:市电断了 → 应急灯亮 → 最差情况还有手电筒。永远保留一条最低限度的通路。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 设计哲学
|
### 设计哲学
|
||||||
|
|
||||||
> **宁可降级,也不能拒绝服务。**
|
> **宁可降级,也不能拒绝服务。**
|
||||||
|
|
||||||
@@ -171,13 +185,9 @@ flowchart TB
|
|||||||
|
|
||||||
每一级降级都意味着功能的减少,但服务的可用性始终得到保障。前端收到 `200 OK` 后,通过 SSE 或轮询获取任务进度,对用户而言体验一致。
|
每一级降级都意味着功能的减少,但服务的可用性始终得到保障。前端收到 `200 OK` 后,通过 SSE 或轮询获取任务进度,对用户而言体验一致。
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 关联文档
|
## 关联文档
|
||||||
|
|
||||||
| 文档 | 关系 |
|
- [[11-Consumer-Producer桥接]] — Consumer 连接 TaskQueue 和 WorkerPool
|
||||||
|------|------|
|
- [[02-协程池]] — WorkerPool 的背压和限流机制
|
||||||
| [11 - Consumer-Producer 桥接](11-consumer-producer.md) | Consumer 连接 TaskQueue 和 WorkerPool |
|
- [[03-任务队列]] — TaskQueue 接口的可插拔设计
|
||||||
| [02 - 协程池](02-worker-pool.md) | WorkerPool 的背压和限流机制 |
|
- [[00-索引]] — 返回文档总览
|
||||||
| [03 - 任务队列](03-task-queue.md) | TaskQueue 接口的可插拔设计 |
|
|
||||||
| [00 - 索引](00-index.md) | 返回文档总览 |
|
|
||||||
|
|||||||
+33
-26
@@ -1,10 +1,17 @@
|
|||||||
# 13. 标签驱动提示词工程
|
---
|
||||||
|
tags: [prompt-engineering, tag-mapping, ai, llm, fallback-pattern, template]
|
||||||
> **一句话概括**:40+ 预定义标签映射到精确的图像生成指令,保障风格一致性与管线友好性。
|
create time: 2026-06-03 11:00
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 处理流程
|
# 13. 标签驱动提示词工程
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
40+ 预定义标签映射到精确的图像生成指令,保障风格一致性与管线友好性。
|
||||||
|
|
||||||
|
## 正文
|
||||||
|
|
||||||
|
### 处理流程
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
flowchart LR
|
||||||
@@ -24,11 +31,11 @@ flowchart LR
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 标签分类体系
|
### 标签分类体系
|
||||||
|
|
||||||
系统内置 40+ 预定义标签,分为 **7 大类别**,每条标签精确映射到一条图像生成指令。
|
系统内置 40+ 预定义标签,分为 **7 大类别**,每条标签精确映射到一条图像生成指令。
|
||||||
|
|
||||||
### 内容类型 — 决定布局与格式
|
#### 内容类型 — 决定布局与格式
|
||||||
|
|
||||||
| 标签 | 映射指令要点 |
|
| 标签 | 映射指令要点 |
|
||||||
|------|-------------|
|
|------|-------------|
|
||||||
@@ -40,7 +47,7 @@ flowchart LR
|
|||||||
| `序列帧` | 连续动画帧,网格排列,标注方向和帧数 |
|
| `序列帧` | 连续动画帧,网格排列,标注方向和帧数 |
|
||||||
| `纸娃娃部件` | 可组合散件,统一比例和锚点 |
|
| `纸娃娃部件` | 可组合散件,统一比例和锚点 |
|
||||||
|
|
||||||
### 美术风格 — 决定渲染技术
|
#### 美术风格 — 决定渲染技术
|
||||||
|
|
||||||
| 标签 | 映射指令要点 |
|
| 标签 | 映射指令要点 |
|
||||||
|------|-------------|
|
|------|-------------|
|
||||||
@@ -50,7 +57,7 @@ flowchart LR
|
|||||||
| `矢量` | 干净几何形状,平滑曲线 |
|
| `矢量` | 干净几何形状,平滑曲线 |
|
||||||
| `扁平` | 无阴影或极少阴影,纯色块面 |
|
| `扁平` | 无阴影或极少阴影,纯色块面 |
|
||||||
|
|
||||||
### 色调配色
|
#### 色调配色
|
||||||
|
|
||||||
| 标签 | 映射指令要点 |
|
| 标签 | 映射指令要点 |
|
||||||
|------|-------------|
|
|------|-------------|
|
||||||
@@ -60,7 +67,7 @@ flowchart LR
|
|||||||
| `柔和` | 低饱和度,温和内敛 |
|
| `柔和` | 低饱和度,温和内敛 |
|
||||||
| `单色` | 单一色相,明暗层次 |
|
| `单色` | 单一色相,明暗层次 |
|
||||||
|
|
||||||
### 线条粗细
|
#### 线条粗细
|
||||||
|
|
||||||
| 标签 | 映射指令要点 |
|
| 标签 | 映射指令要点 |
|
||||||
|------|-------------|
|
|------|-------------|
|
||||||
@@ -69,7 +76,7 @@ flowchart LR
|
|||||||
| `中等` | 1-2px,清晰明确 |
|
| `中等` | 1-2px,清晰明确 |
|
||||||
| `粗线` | 2-4px,粗犷有力 |
|
| `粗线` | 2-4px,粗犷有力 |
|
||||||
|
|
||||||
### 场景氛围
|
#### 场景氛围
|
||||||
|
|
||||||
| 标签 | 映射指令要点 |
|
| 标签 | 映射指令要点 |
|
||||||
|------|-------------|
|
|------|-------------|
|
||||||
@@ -80,7 +87,7 @@ flowchart LR
|
|||||||
| `水下` | 珊瑚、水草、气泡 |
|
| `水下` | 珊瑚、水草、气泡 |
|
||||||
| `沙漠` | 沙丘、仙人掌、绿洲 |
|
| `沙漠` | 沙丘、仙人掌、绿洲 |
|
||||||
|
|
||||||
### 光照效果
|
#### 光照效果
|
||||||
|
|
||||||
| 标签 | 映射指令要点 |
|
| 标签 | 映射指令要点 |
|
||||||
|------|-------------|
|
|------|-------------|
|
||||||
@@ -89,7 +96,7 @@ flowchart LR
|
|||||||
| `戏剧` | 强烈明暗对比,聚光灯效果 |
|
| `戏剧` | 强烈明暗对比,聚光灯效果 |
|
||||||
| `霓虹` | 高饱和彩色光源,赛博朋克辉光 |
|
| `霓虹` | 高饱和彩色光源,赛博朋克辉光 |
|
||||||
|
|
||||||
### 情绪基调
|
#### 情绪基调
|
||||||
|
|
||||||
| 标签 | 映射指令要点 |
|
| 标签 | 映射指令要点 |
|
||||||
|------|-------------|
|
|------|-------------|
|
||||||
@@ -101,7 +108,7 @@ flowchart LR
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 精灵图特殊处理
|
### 精灵图特殊处理
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TD
|
flowchart TD
|
||||||
@@ -121,9 +128,13 @@ flowchart TD
|
|||||||
|
|
||||||
这些指令对下游的 `SplitSprite` 切割算法至关重要。
|
这些指令对下游的 `SplitSprite` 切割算法至关重要。
|
||||||
|
|
||||||
|
> [!question] 为什么精灵图需要特殊的网格布局指令?
|
||||||
|
>
|
||||||
|
> 如果不指定间隙要求,AI 生成的图片往往会让人物之间几乎没有空隙——人类艺术家这样做是为了最大化利用画布,但机器无法从中准确推断分割边界。**强制纯白间隙 = 人为制造"裂缝"**,让投影检测算法可以像翻书页一样逐页分离内容。这就是典型的「用约束换精度」的设计思路。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 未知标签回退
|
### 未知标签回退
|
||||||
|
|
||||||
当用户输入系统未预定义的标签时,不会报错,而是降级为通用风格描述:
|
当用户输入系统未预定义的标签时,不会报错,而是降级为通用风格描述:
|
||||||
|
|
||||||
@@ -141,7 +152,7 @@ func tagToInstruction(tag string) string {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## PromptOptimizer 节点
|
### PromptOptimizer 节点
|
||||||
|
|
||||||
PromptOptimizer 是 Eino 管线的第一个节点,负责将标签指令组装为最终提示词。
|
PromptOptimizer 是 Eino 管线的第一个节点,负责将标签指令组装为最终提示词。
|
||||||
|
|
||||||
@@ -186,13 +197,13 @@ flowchart LR
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## LLM 回退机制
|
### LLM 回退机制
|
||||||
|
|
||||||
当 LLM 不可用时(API Key 未配置 / 网络故障),系统自动降级到模板生成:
|
当 LLM 不可用时(API Key 未配置 / 网络故障),系统自动降级到模板生成:
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TD
|
flowchart TD
|
||||||
A["callLLMRefine"] --> B{"API Key\n已配置?"}
|
A["callLLMRefine"] --> B{"API Key 已配置?"}
|
||||||
B -->|"否"| F["fallbackRefine\n模板回退"]
|
B -->|"否"| F["fallbackRefine\n模板回退"]
|
||||||
B -->|"是"| C["调用 Chat API"]
|
B -->|"是"| C["调用 Chat API"]
|
||||||
C --> D{"调用成功?"}
|
C --> D{"调用成功?"}
|
||||||
@@ -204,13 +215,9 @@ flowchart TD
|
|||||||
|
|
||||||
模板回退同样遵循标签驱动逻辑,保证即使没有 LLM 参与,生成的提示词也具备结构化和一致性。
|
模板回退同样遵循标签驱动逻辑,保证即使没有 LLM 参与,生成的提示词也具备结构化和一致性。
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 关联文档
|
## 关联文档
|
||||||
|
|
||||||
| 文档 | 关系 |
|
- [[05-生成管线]] — PromptOptimizer 是管线第一阶段
|
||||||
|------|------|
|
- [[06-精灵图处理]] — 网格布局指令影响切割算法
|
||||||
| [05 - 生成管线](05-generation-pipeline.md) | PromptOptimizer 是管线第一阶段 |
|
- [[01-系统总览]] — 提示词工程在整体架构中的位置
|
||||||
| [06 - 精灵图处理](06-sprite-processing.md) | 网格布局指令影响切割算法 |
|
- [[00-索引]] — 返回文档总览
|
||||||
| [01 - 系统总览](01-system-overview.md) | 提示词工程在整体架构中的位置 |
|
|
||||||
| [00 - 索引](00-index.md) | 返回文档总览 |
|
|
||||||
|
|||||||
+26
-25
@@ -1,10 +1,17 @@
|
|||||||
# 14. 部署架构
|
---
|
||||||
|
tags: [deployment, docker-compose, monitoring, prometheus, grafana, devops]
|
||||||
> **一句话概括**:Docker Compose 编排 + Prometheus 监控 + Grafana 可视化 + 自动化部署脚本。
|
create time: 2026-06-03 11:05
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 容器拓扑
|
# 14. 部署架构
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
Docker Compose 编排 + Prometheus 监控 + Grafana 可视化 + 自动化部署脚本。
|
||||||
|
|
||||||
|
## 正文
|
||||||
|
|
||||||
|
### 容器拓扑
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TB
|
flowchart TB
|
||||||
@@ -49,9 +56,7 @@ flowchart TB
|
|||||||
style GF fill:#f46800,stroke:#333,color:#fff
|
style GF fill:#f46800,stroke:#333,color:#fff
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
### 服务组成
|
||||||
|
|
||||||
## 服务组成
|
|
||||||
|
|
||||||
| 服务 | 镜像 | 端口 | 职责 |
|
| 服务 | 镜像 | 端口 | 职责 |
|
||||||
|------|------|------|------|
|
|------|------|------|------|
|
||||||
@@ -64,9 +69,9 @@ flowchart TB
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 监控栈
|
### 监控栈
|
||||||
|
|
||||||
### Prometheus 配置
|
#### Prometheus 配置
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# deploy/prometheus/prometheus.yml
|
# deploy/prometheus/prometheus.yml
|
||||||
@@ -80,7 +85,7 @@ scrape_configs:
|
|||||||
|
|
||||||
Prometheus 每 **10 秒**抓取一次后端的 `/metrics` 端点,采集全部 35+ 指标。
|
Prometheus 每 **10 秒**抓取一次后端的 `/metrics` 端点,采集全部 35+ 指标。
|
||||||
|
|
||||||
### Grafana 自动化
|
#### Grafana 自动化
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
flowchart LR
|
||||||
@@ -101,7 +106,7 @@ Grafana 通过 provisioning 机制自动加载:
|
|||||||
- **数据源配置** — 指向 Prometheus 实例
|
- **数据源配置** — 指向 Prometheus 实例
|
||||||
- **仪表盘 JSON** — 预定义的 3 个仪表盘
|
- **仪表盘 JSON** — 预定义的 3 个仪表盘
|
||||||
|
|
||||||
### 告警规则
|
#### 告警规则
|
||||||
|
|
||||||
10 条告警规则覆盖全栈关键指标:
|
10 条告警规则覆盖全栈关键指标:
|
||||||
|
|
||||||
@@ -120,7 +125,7 @@ Grafana 通过 provisioning 机制自动加载:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 部署脚本
|
### 部署脚本
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TD
|
flowchart TD
|
||||||
@@ -142,7 +147,7 @@ flowchart TD
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 配置管理
|
### 配置管理
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
flowchart LR
|
||||||
@@ -174,13 +179,13 @@ flowchart LR
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 网络与存储
|
### 网络与存储
|
||||||
|
|
||||||
### Docker 网络
|
#### Docker 网络
|
||||||
|
|
||||||
所有服务加入 `gen2d-v2-net` 桥接网络,容器间通过服务名互相访问。
|
所有服务加入 `gen2d-v2-net` 桥接网络,容器间通过服务名互相访问。
|
||||||
|
|
||||||
### 数据卷
|
#### 数据卷
|
||||||
|
|
||||||
| 卷名 | 挂载点 | 用途 |
|
| 卷名 | 挂载点 | 用途 |
|
||||||
|------|--------|------|
|
|------|--------|------|
|
||||||
@@ -188,13 +193,9 @@ flowchart LR
|
|||||||
| MySQL data | 默认 | 数据库持久化 |
|
| MySQL data | 默认 | 数据库持久化 |
|
||||||
| Redis data | 默认 | 缓存持久化 |
|
| Redis data | 默认 | 缓存持久化 |
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 关联文档
|
## 关联文档
|
||||||
|
|
||||||
| 文档 | 关系 |
|
- [[07-可观测性]] — Prometheus 指标与 Grafana 仪表盘详情
|
||||||
|------|------|
|
- [[15-配置级联机制]] — YAML / ENV / Default 三层配置机制
|
||||||
| [07 - 可观测性](07-observability.md) | Prometheus 指标与 Grafana 仪表盘详情 |
|
- [[01-系统总览]] — 部署架构在整体系统中的位置
|
||||||
| [15 - 配置级联](15-config-cascade.md) | YAML / ENV / Default 三层配置机制 |
|
- [[00-索引]] — 返回文档总览
|
||||||
| [01 - 系统总览](01-system-overview.md) | 部署架构在整体系统中的位置 |
|
|
||||||
| [00 - 索引](00-index.md) | 返回文档总览 |
|
|
||||||
|
|||||||
+22
-19
@@ -1,10 +1,17 @@
|
|||||||
# 15. 配置级联机制
|
---
|
||||||
|
tags: [config, viper, environment-variables, yaml, deployment, configuration-management]
|
||||||
> **一句话概括**:Viper 三层配置级联 — YAML 文件 -> 环境变量 -> 默认值,一处配置随处运行。
|
create time: 2026-06-03 11:10
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 配置加载流程
|
# 15. 配置级联机制
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
Viper 三层配置级联 — YAML 文件 -> 环境变量 -> 默认值,一处配置随处运行。
|
||||||
|
|
||||||
|
## 正文
|
||||||
|
|
||||||
|
### 配置加载流程
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
flowchart LR
|
||||||
@@ -41,7 +48,7 @@ flowchart LR
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 配置结构体
|
### 配置结构体
|
||||||
|
|
||||||
```go
|
```go
|
||||||
type Config struct {
|
type Config struct {
|
||||||
@@ -73,7 +80,7 @@ type Config struct {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 环境变量绑定
|
### 环境变量绑定
|
||||||
|
|
||||||
每个配置字段都有对应的环境变量绑定,命名规则为 `GEN2D_` 前缀 + 大写下划线格式:
|
每个配置字段都有对应的环境变量绑定,命名规则为 `GEN2D_` 前缀 + 大写下划线格式:
|
||||||
|
|
||||||
@@ -104,7 +111,7 @@ func bindEnvVars(v *viper.Viper) {
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 使用场景
|
### 使用场景
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TD
|
flowchart TD
|
||||||
@@ -140,12 +147,12 @@ flowchart TD
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 加载过程详解
|
### 加载过程详解
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
sequenceDiagram
|
sequenceDiagram
|
||||||
participant Main as main.go
|
participant Main as main.go
|
||||||
participant Viper
|
participant Viper as Viper
|
||||||
participant YAML as config.yaml
|
participant YAML as config.yaml
|
||||||
participant ENV as 环境变量
|
participant ENV as 环境变量
|
||||||
participant Cfg as Config Struct
|
participant Cfg as Config Struct
|
||||||
@@ -175,7 +182,7 @@ sequenceDiagram
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 与部署的关系
|
### 与部署的关系
|
||||||
|
|
||||||
配置级联机制与部署架构紧密配合:
|
配置级联机制与部署架构紧密配合:
|
||||||
|
|
||||||
@@ -185,13 +192,9 @@ sequenceDiagram
|
|||||||
| `docker compose` | `.env` 文件注入 | `env_file: ./backend/.env` |
|
| `docker compose` | `.env` 文件注入 | `env_file: ./backend/.env` |
|
||||||
| Kubernetes | ConfigMap + Secret | `GEN2D_DSN` 从 Secret 注入 |
|
| Kubernetes | ConfigMap + Secret | `GEN2D_DSN` 从 Secret 注入 |
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 关联文档
|
## 关联文档
|
||||||
|
|
||||||
| 文档 | 关系 |
|
- [[14-部署架构]] — 配置管理在部署中的应用
|
||||||
|------|------|
|
- [[01-系统总览]] — 配置在启动流程中的位置
|
||||||
| [14 - 部署架构](14-deployment.md) | 配置管理在部署中的应用 |
|
- [[02-协程池]] — WorkerPoolConfig 控制并发参数
|
||||||
| [01 - 系统总览](01-system-overview.md) | 配置在启动流程中的位置 |
|
- [[00-索引]] — 返回文档总览
|
||||||
| [02 - 协程池](02-worker-pool.md) | WorkerPoolConfig 控制并发参数 |
|
|
||||||
| [00 - 索引](00-index.md) | 返回文档总览 |
|
|
||||||
|
|||||||
Reference in New Issue
Block a user