vault backup: 2026-06-03 10:30:42

This commit is contained in:
2026-06-03 10:30:42 +08:00
parent 2e207dc8fb
commit b5d9e44f0b
16 changed files with 661 additions and 507 deletions
+76 -49
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 端点的中间件配置
+38 -37
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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) | 返回文档总览 |