Files
cs-note/hzh/Gen2D/QA.md
T

160 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Gen2D — Q&A
> 按模块分布编排,贴近实际答辩场景。每个 Answer 已精简适配 PPT 展示。
---
## 一、系统架构(Q1–Q4)
**Q1:Gen2D 的整体架构是什么?**
A:采用经典分层架构,从浏览器请求到外部依赖共六层:Gin HTTP Server → Handler → Service 层(Eino Pipeline)→ 基础设施层(协程池/队列/事件总线)→ 领域模型 → 外部依赖(MySQL/Redis/CDN/AI API)。各层职责清晰、可独立替换。
**Q2:为什么选择异步任务 + SSE 推送的组合?**
A:AI 生成耗时 10~120 秒,如果同步调用会耗尽服务器连接。异步提交让 API 快速返回 taskId,后续通过 SSE 长连接推送进度,客户端可以自由离开并在后台接收更新。
**Q3:系统的依赖注入方式是什么?为什么没有使用 Wire/Fx 等框架?**
A:采用轻量级的 Set*/Init* 函数注入,在 main.go 中约 220 行完成全部注入。项目规模可控,框架级 DI 的复杂度收益比不高。
**Q4:配置管理是如何设计的?**
A:使用 Viper 实现三层级联:环境变量 > YAML 配置文件 > 代码默认值。容器化部署时可通过环境变量覆盖,开发时使用 YAML 集中管理,零配置也可启动运行。
---
## 二、并发控制与协程池(Q5–Q8)
**Q5:协程池的核心参数和默认值是什么?**
A:Worker 数量 = NumCPU × 4,任务队列容量 = 100,单用户最大并发数 = 2。IO 密集型场景推荐 4 倍 CPU 核数。
**Q6:什么是背压保护?Gen2D 为什么选择非阻塞拒绝而不是阻塞等待?**
A:当协程池队列满时立即返回 ErrPoolFull 错误并返回 HTTP 503,而不是等待有空位。因为用户期望快速反馈,且 AI 生成场景中用户可以重新点击提交——这种短暂的操作成本远低于服务器因连接堆积导致的雪崩风险。
**Q7:Per-user 限流的作用和实现机制是什么?**
A:防止单个用户占满整个协程池。实现上在 Submit 时就增加 userActive 计数预留槽位,任务完成后释放。这样即使 channel 还未取出任务,也能保证槽位一致性。
**Q8:协程池如何优雅关闭?**
A:收到 SIGINT/SIGTERM 信号后,先停止接受新任务(consumeCancel),再通过 wg.Wait 等待所有正在执行的 Worker 完成,超时时间为 30 秒。超时则部分任务可能丢失。
---
## 三、任务队列(Q9–Q11)
**Q9:任务队列的可插拔设计是什么意思?**
A:定义了统一的 TaskQueue 接口(Submit/Consume/Close),支持 MemoryQueue 和 RabbitMQQueue 两种实现。通过配置中的 `driver` 字段一行切换,单机开发用内存队列,生产环境用 RabbitMQ。
**Q10:MemoryQueue 和 RabbitMQQueue 的主要区别是什么?**
A:MemoryQueue 零依赖但无持久化和重试,进程重启任务丢失;RabbitMQQueue 消息持久化到磁盘、支持失败重试和手动 ACK,适合多机部署的生产环境。
**Q11:任务消息的结构中包含哪些关键字段?**
A:包含 TaskID、UserID、ProjectID、Input(PipelineInput)、CreatedAt 和 RetryCount。其中 Input 直接嵌入管线输入结构,反序列化后可直接传入;RetryCount 用于判断是否超过最大重试次数。
---
## 四、生成管线(Q12–Q16)
**Q12:生成管线的四个阶段分别是什么?**
A:第一阶段 PromptOptimizer(提示词优化,纯 CPU <1ms),第二阶段 AssetGenerator(AI 出图,IO 密集 10-120s),第三阶段 QualitySupervisor(质量检查 1-5s),第四阶段 FormatAdapter(格式适配/Cut/GIF 1-3s)。
**Q13:质量检测最多重试 3 次的策略依据是什么?**
A:第 1 次失败通常是 LLM 波动或偶发噪声,重试大概率通过;第 2 次失败说明提示词不够精确,重新优化后改善;第 3 次仍失败表示当前参数确实无法生成合格图片。超过 3 次降级到 FormatAdapter——宁可降级不可阻塞。
**Q14:AssetGenerator 的三级回退逻辑是什么?**
A:优先使用任务级参考图进行图生图,其次使用工程级参考图 URL 下载后图生图,最后回退到纯文生图。任意一级失败都尝试下一级,确保任务不阻塞。
**Q15:管线的安全机制有哪些?**
A:包括三个层面——Eino Graph 设置 WithMaxRunSteps(20) 防止无限循环,WorkerPool 设置 context.WithTimeout(10min) 控制任务超时,以及 context.Canceled 支持优雅关闭时取消执行中的管线。
**Q16:管线的全局状态是怎么管理的?**
A:通过 Eino 的 WithGenLocalState 注入 PipelineState 全局状态对象,各节点通过 StatePreHandler / StatePostHandler 读写状态。状态包含原始输入、最终提示词、质检结果、重试次数和路由目标。
---
## 五、精灵图处理(Q17–Q19)
**Q17:精灵图处理管线包含哪几个步骤?**
A:背景移除(白底/绿幕)→ 投影检测分割 → MinFillRatio 过滤空白帧 → trimAlpha 裁剪透明边框 → padToLargest 底部居中对齐 → GIF 预览生成。
**Q18:两阶段投影算法相比传统方法有什么优势?**
A:传统全局投影会因为武器、尾巴、翅膀等突出物被其他行"稀释"导致切割不准。两阶段算法先用全局行投影检测水平间隙,再在每个行段内独立计算列密度——相当于把二维问题拆解为多个一维子问题,避免跨行干扰。
**Q19:GIF Maker 的防鬼影机制如何实现?**
A:设置 DisposalBackground,每帧渲染前清除前一帧的内容。同时设置调色板索引 0 为透明色,LoopCount=0 实现无限循环播放。
---
## 六、SSE 实时推送(Q20–Q22)
**Q20:EventBus 的订阅模式有哪几种?**
A:两种——Subscribe(taskID) 按任务订阅,Buffer 容量 16,用于单任务实时进度;SubscribeAll() 全局订阅,Buffer 容量 64,用于工程级查看所有任务的混合视图,在 Handler 层按 projectID 过滤。
**Q21:为什么 EventBus 的 Publish 使用 non-blocking send?**
A:使用 select default 模式,当消费者 channel 缓冲满时静默丢弃事件。这是为了防止慢消费者拖慢管线执行速度,保证 Pipeline 的处理效率不受 SSE 推送影响。
**Q22:SSE 连接在什么条件下会自动关闭?**
A:两种情况——收到终态事件(completed/failed)时自动关闭;客户端断开连接时通过 c.Request.Context().Done() 触发 Unsubscribe 并退出。
---
## 七、可观测性(Q23–Q25)
**Q23:Gen2D 的可观测性体系覆盖哪些层面?**
A:共 35 个 Prometheus 指标分为六大组——HTTP 层、限流层、任务队列层、协程池层、Pipeline 业务层、基础设施层。配套 3 个 Grafana 仪表盘(Overview/Pipeline/Infrastructure)和 10 条告警规则。
**Q24:使用 FullPath() 而不是实际路径采集 HTTP 指标有什么用意?**
A:使用路由模板 `/api/v1/tasks/:taskId` 而非实际路径 `/api/v1/tasks/abc123`,避免高基数标签导致 Prometheus 内存爆炸。
**Q25:哪些告警是 Critical 级别需要立即处理的?**
A:PipelineSuccessRateLow(成功率低于 90%)、RedisDown(连接池为零)、RabbitMQDisconnected(断连)和 HighErrorRate(5xx 错误率超 5%)。这些表示核心功能或服务健康度出现严重问题。
---
## 八、限流与降级(Q26–Q28)
**Q26:双层限流的设计思路是什么?**
A:全局限流在前先检查系统总体配额(如 24 小时 500 次),通过后检查用户限流(如 24 小时 15 次)。这样可以先防止单一用户耗尽全局资源,再在用户层面做精细控制。
**Q27:为什么限流器选择 Fail-Open 而不是 Fail-Close?**
A:在"偶尔超限"和"完全不可用"之间,优先保可用性。Redis 故障时放行请求,超出配额的用户后续可以通过账单追缴;如果选择 Fail-Close,Redis 故障会导致所有用户都无法使用服务。
**Q28:三级降级链的第一、二、三级分别是什么?**
A:第一级 TaskQueue(持久化排队 + 分布式能力),第二级 WorkerPool(有界并发 + 用户隔离),第三级 Legacy FIFO(零配置串行兜底)。每一级都是前一级功能的超集,降级过程渐进、服务始终可用。
---
## 九、提示词工程(Q29–Q30)
**Q29:标签驱动提示词工程的工作原理是什么?**
A:内置 40+ 预定义标签分为 7 大类别(内容类型、美术风格、色调配色、线条粗细、场景氛围、光照效果、情绪基调),每条标签映射到精确的图像生成指令。系统将这些指令与用户描述、风格约束和技术参数组装为三段式提示词。
**Q30:为什么精灵图的网格布局需要特殊处理?**
A:如果不指定间隙要求,AI 生成的图片往往人物之间几乎没有空隙——人类艺术家为了最大化利用画布这样做,但机器无法从中推断分割边界。强制纯白间隙等于人为制造"裂缝",让投影检测算法可以可靠地分离每帧内容。