MG-03-004 系统架构
状态说明:以当前有效边界为准。供应执行规则与历史实现证据保留;客户身份、售价、结算、全量旧跨仓契约及图像开放计划不代表当前验收范围。
1. 架构目标
- 只承载图像和视频生成;
- 首版按单机 Docker Compose 运行,API 与 Worker 按角色拆分;
- PostgreSQL 保存正确性,Redis 只做可恢复加速;
- 同步图像保持低延迟;
- 异步视频和图像支持持久排队、换供给和查询;
- Channel 和 Supply 故障隔离;
- Provider 适配器与调度策略解耦;
- 图像和视频业务实现解耦;
- 单次生产切换替换旧媒体入口。
2. 运行组件
gateway-api
管理 API
用户 API
模型广场
同步图像执行
异步任务创建与查询
内容交付入口
async-dispatcher
领取异步任务
调度 Supply
原子预占容量
提交 Provider
video-poller
查询已经提交的视频 Attempt
映射状态
安排下一次查询
image-async-poller
只为异步图像 Provider 启用
result-worker(首版由对应媒体 Worker 内部执行)
拉取 Provider 临时结果
校验类型、大小和哈希
异步图像先写入持久 Result Spool
写入平台 S3/R2
创建 MediaOutput
reconciler
恢复过期租约
恢复 Redis 丢失的可调度事件
处理 submission_uncertain 对账
检查孤儿任务和结果
health-worker
有限主动探测
半开恢复
模型列表变化检查
告警聚合初期由一个 Go 二进制通过子命令启动不同角色。所有角色位于同一台主机;API、 Dispatcher、Poller 和 Reconciler 共享一个只允许网关 UID 访问的 Docker 命名卷作为 Result Spool。切换到多机前必须先把该接力区替换为具备同等原子性和持久性的共享存储, 不能直接横向扩容。
3. 请求路径
同步图像
Client
→ gateway-api
→ Auth/Model Access
→ Image Normalization
→ Image Scheduler
→ Capacity Lease
→ Image Provider Adapter
→ Result Validation/Storage
→ MediaRequest + Attempt + Output
→ 同步响应同步路径不经过异步队列。Provider 调用前后使用短事务保存状态,但 Provider I/O 不在事务内。
异步视频和图像
Client
→ gateway-api
→ Auth/Model Access
→ Media-specific Normalization
→ MediaRequest + AsyncTask + Outbox
→ 返回平台 Task ID
→ Redis Stream 唤醒 Dispatcher
→ Scheduler/Capacity Lease
→ Provider Submit
→ Poller
→ Result Worker
→ Platform Output
→ Task succeeded创建 MediaRequest、AsyncTask 和 Outbox 必须同事务。Redis 消息丢失时 Reconciler 从 PostgreSQL Outbox 恢复。
4. 模块边界
建议目录:
cmd/
gateway/
internal/
identity/
auth/
users/
channels/
catalog/
publicmodels/
health/
capacity/
cost/
records/
assets/
alerts/
audit/
image/
api/
domain/
capability/
scheduler/
executor/
provider/
result/
video/
api/
domain/
capability/
scheduler/
dispatcher/
poller/
provider/
result/
fallback/
async/
tasks/
outbox/
queue/
reconciler/
platform/
postgres/
redis/
objectstore/
crypto/
observability/
web/
src/
components/
layouts/
features/
dashboard/
model-square/
api-keys/
records/
docs/
channels/
supplies/
public-models/
users/
health/
settings/5. 依赖方向
HTTP/Workers → Application Services → Domain Ports
Platform Implementations → Domain Ports
Provider Adapters → Image or Video Provider Ports
Image -/→ Video
Video -/→ Image
Shared Modules -/→ Image/Video implementations禁止建立一个包含所有图像和视频参数的通用 CanonicalMediaRequest。共享层只能保存
媒体类型、Request ID、Supply ID、成本、健康、容量和输出资产等稳定信息。
6. PostgreSQL
PostgreSQL 保存:
- 用户、客户组和 Key;
- Channel、Credential Version、Supply;
- 图像/视频能力和 Cost Rule Version;
- Public Model、Binding、Policy、Degradation Rule;
- MediaRequest、AsyncTask、Attempt、Output;
- 路由快照、健康事件、告警和审计;
- Outbox 和幂等 Registry。
首版优先保证简单可靠的主键、唯一约束和索引。是否对使用记录与事件表分区必须由 真实负载测试和保留量决定,不能为了继承旧设计而提前增加跨分区唯一性复杂度。
单机 Compose 保持 synchronous_commit=on,不以降低提交持久性换吞吐。默认启用
WAL 压缩、2 GiB max_wal_size 和 1 ms commit_delay,用更少的检查点与组提交
承载异步创建和 Worker 并发写入。网关进程默认各使用 15 个 PostgreSQL 连接,
PostgreSQL 默认允许 150 个连接,使单机可以有界扩展多个 Worker 副本而不耗尽连接;
PostgreSQL 默认可使用 3 个 CPU,在四核 Docker Desktop 上为数据库写入保留主要
算力,同时给 API 与 Worker 留出一个核的调度空间。这些值均可由 .env 调整。
磁盘容量必须在 Result Spool、对象存储和数据库数据之外,额外预留不小于
max_wal_size 的空间。
管理员使用记录默认按 media_requests(created_at DESC, id DESC) 索引读取最新页;
不能在列表请求中对持续增长的整表排序。媒体、模式、状态、模型和时间过滤继续由
Repository 组合,详情才读取 Attempt、Output 与生命周期事件。
7. Redis
Redis 保存可重建状态:
- 异步任务唤醒 Stream;
- Dispatcher Consumer Group;
- Channel/Supply 实时健康缓存;
- Channel/Supply Capacity Lease;
- 短期模型广场与目录缓存;
- 限流计数;
- 告警去重窗口。
Redis 不保存唯一 Task、唯一 Attempt、最终状态或唯一计费事实。
客户组与 API Key 的异步并发 Permit 以 PostgreSQL 状态为恢复依据,以 Redis
有序集合原子执行实时占用。终态释放先幂等删除 Redis 占用,再把 PostgreSQL
releasing 提交为 released;任一步中断都由 Reconciler 以每批 1,000、最多
10 批、32 路并发的固定上限恢复。Redis 冷启动按 PostgreSQL 活跃 Task 重建,重建
窗口 fail closed,不能超卖。重建锁的等待方复用同一个 ready marker,实际 Permit
活动持续刷新其 TTL;PostgreSQL 通过客户组与释放时间的部分索引有界清理历史状态,
避免历史已释放 Permit 令冷启动退化为整表扫描。
8. 对象存储
支持三个逻辑桶:
temporary-materials
异步请求输入素材和 upload-ref 临时对象
primary-results
正式输出主位置
result-replicas
正式输出副本或灾备具体 S3/R2 账户和桶数量可以不同,但逻辑职责不能混用。
公开内容使用稳定 Asset ID。签名 URL 按请求生成,不写入数据库。
异步图像的 Provider 成功结果先写入单机持久 Result Spool:
- 文件先写私有临时文件、校验大小和 SHA-256、
fsync,再以不可覆盖的原子操作发布; - PostgreSQL 只保存 Attempt/Output、相对文件键、类型、大小、摘要和失败次数,不保存 Provider URL、Base64 或 Credential;
- 对象存储短时失败只重试 Spool → S3/R2 发布,不再次调用 Provider;
- 达到发布失败上限后 Attempt/Task 稳定失败并产生去重告警;
- PostgreSQL 容量预留限制 Spool 正式文件总量;首版默认 32 GiB,预留额外磁盘空间 承担并发下载临时文件;
- 成功后删除 Spool 行和文件;无数据库记录的文件由 Reconciler 在宽限期后清理;
- 同步图像不使用持久接力和异步重试,仍在同一个 HTTP 请求内成功或失败。
9. 高并发
- PostgreSQL 是权威状态;Redis 可重建;Result Spool 是单机持久媒体接力状态;
- Channel 和 Supply 容量使用原子租约;
- 用户同步/异步并发分别限制;
- 队列按可用容量事件唤醒,不只靠固定轮询;
- Dispatcher/Poller 的代码并发上限为 100,单机默认每进程有效并发进一步受 PostgreSQL 连接池 15 限制;可以增加有界 Compose Worker 副本;
- Dispatcher 每次只领取能在租约内处理的任务,领取候选先按批次上限截断,再申请 PostgreSQL Advisory Lock;
- 任务领取、Attempt 创建和 Outbox 使用数据库原子条件更新;
- Provider 429 进入 Supply 冷却,不形成全局重试风暴;
- 每个 Task 最多两次 Provider 提交;
- Redis 故障时新的异步创建 fail closed 且不留下孤儿记录;已经提交的 Task 仍以 PostgreSQL 为权威,Redis 恢复后重新投递和重建 Permit。
10. 可观测性
指标最少包括:
- 请求量、成功率和延迟,按媒体/模型/执行模式;
- 队列深度、最老等待时间和调度吞吐;
- Channel/Supply 健康状态;
- Supply 并发容量和利用率;
- Provider 错误分类;
- 重试率和降级率;
- 预计成本、实际成本和紧急兜底成本;
- Provider 成功但结果转存失败;
- submission_uncertain 数量与年龄。
日志使用结构化 JSON;Trace 贯穿 Request、Task、Attempt、Provider 和 Result。 秘密、完整素材 URL、Base64 和签名 URL不得进入日志与 Trace。