87 lines
5.3 KiB
Markdown
87 lines
5.3 KiB
Markdown
---
|
||
name: model-call-retry
|
||
overview: 为抠图(Matting)与分类(Classification)两个模型调用增加可配置的自动重试机制,默认最大重试 3 次;配置项分别加到 MattingModel 与 ClassificationModel 配置段下,并在衣物异步处理流程中接入重试逻辑。
|
||
todos:
|
||
- id: add-retry-config
|
||
content: 在 section.go 结构体及 config.yaml、config.example.yaml 中为 MattingModel/ClassificationModel 增加 MaxRetry 配置
|
||
status: completed
|
||
- id: default-retry-value
|
||
content: 在 main.go 读取配置后对两个模型 MaxRetry
|
||
status: completed
|
||
dependencies:
|
||
- add-retry-config
|
||
---
|
||
|
||
## 产品概述
|
||
|
||
为衣物图片处理流水线中的抠图(Matting)与分类(Classification)两个模型调用增加自动重试能力,降低下游模型服务偶发失败导致的衣物处理失败率。重试次数通过配置文件分别控制,默认最大重试 3 次。
|
||
|
||
## 核心功能
|
||
|
||
- 抠图调用失败时自动重试,默认最大重试 3 次(总尝试 4 次)
|
||
- 分类调用失败时自动重试,默认最大重试 3 次(总尝试 4 次)
|
||
- 重试次数分别配置在 `MattingModel` 与 `ClassificationModel` 两个配置段下,配置缺失或小于等于 0 时兜底为 3
|
||
- 全部重试失败后维持现有失败处理:抠图失败回退上传额度并标记失败;分类失败保留已抠图结果(processed_image)并标记失败,便于用户手动补全
|
||
- 删除处理中衣物不回退额度的既有防滥用设计保持不变(不做修改)
|
||
|
||
## 技术栈
|
||
|
||
- Go 1.27(使用泛型、`log/slog`、`context` + `time.After`)
|
||
- 配置:viper(复用现有 `setting.ReadSection` 读取模式)
|
||
- 日志:`log/slog`(复用现有 `slog.WarnContext` / `slog.ErrorContext`)
|
||
|
||
## 实现方案
|
||
|
||
### 总体策略
|
||
|
||
复用现有配置读取与兜底模式,在配置结构体、配置文件中增加 `MaxRetry` 字段;在异步处理函数 `processImageAsync` 中新增一个泛型重试辅助函数 `retryCall`,包裹抠图与分类两处调用,实现失败自动重试,失败分支逻辑保持不变。
|
||
|
||
### 关键设计
|
||
|
||
1. **配置层(最小改动)**
|
||
|
||
- `pkg/setting/section.go` 的 `MattingModelSettingS`、`ClassificationModelSettingS` 各新增 `MaxRetry int` 字段。
|
||
- `configs/config.yaml`(实际配置)与 `configs/config.example.yaml`(示例配置)的 `MattingModel`、`ClassificationModel` 段各新增 `MaxRetry: 3`。
|
||
- `main.go` 在读取 `MattingModel`、`ClassificationModel` 后,对 `MaxRetry <= 0` 兜底为 3,与现有 `MaxSizeMB <= 0` 兜底 20、`Model == ""` 兜底 `hy4` 的模式保持一致。
|
||
|
||
2. **重试层(泛型辅助函数)**
|
||
|
||
- 在 `internal/service/clothes.go` 新增 `retryCall[T any]` 泛型函数:循环尝试 `maxRetry+1` 次;每次失败记录 `slog.WarnContext`;重试间隔使用 `select` + `time.After(1s)` + `ctx.Done()`,保证 ctx 到期时提前终止。
|
||
- 抠图与分类调用分别用 `retryCall` 包裹,`maxRetry` 分别取 `global.MattingModelSetting.MaxRetry` 与 `global.ClassificationModelSetting.MaxRetry`。
|
||
- `fn` 闭包捕获外部 `ctx`,每次调用内部会重建 multipart body,无需额外重置状态。
|
||
|
||
3. **失败分支保持不变**
|
||
|
||
- 抠图全部重试失败后仍调用 `markProcessFailed`(标记失败 + 回退上传额度)。
|
||
- 分类全部重试失败后仍保留 `processed_image` 并标记 `ImageProcessFailed`(不回退额度,允许用户手动补全)。
|
||
|
||
### 关键代码结构
|
||
|
||
```
|
||
// retryCall 对 fn 进行最多 maxRetry 次额外重试(总尝试 maxRetry+1 次)。
|
||
// 重试间隔 1s;ctx 到期则提前终止并返回 ctx.Err()。
|
||
func retryCall[T any](ctx context.Context, maxRetry int, name string, fn func() (T, error)) (T, error)
|
||
|
||
// 调用示例
|
||
processedData, err := retryCall(ctx, global.MattingModelSetting.MaxRetry, "matting", func() ([]byte, error) {
|
||
return mattingClient.Matting(ctx, imageData)
|
||
})
|
||
```
|
||
|
||
## 实现注意
|
||
|
||
- **MaxRetry 语义**:失败后的额外重试次数,总尝试次数 = MaxRetry + 1;默认 3 即总尝试 4 次。
|
||
- **超时预算**:异步 ctx 总超时为 3 分钟,单次 HTTP 客户端超时为 120s。`retryCall` 通过 `ctx.Done()` 检查避免超时后继续重试;瞬时失败(连接拒绝、偶发非 200)通常快速返回,3 分钟预算足够。
|
||
- **并发限流不受影响**:`imageProcessSem` 信号量在 `processImageAsync` 入口已获取,重试发生在同一 goroutine 内,不会放大并发。
|
||
- **blast radius 控制**:仅新增配置字段与重试辅助函数,不修改 `DeleteByUUID` 额度回退逻辑;`config.yaml` 中重复的 `UserDefaultQuota` 段为既有问题,本次不处理。
|
||
|
||
## 目录结构
|
||
|
||
```
|
||
zt-closet-backend/
|
||
├── pkg/setting/section.go # [MODIFY] MattingModelSettingS、ClassificationModelSettingS 各新增 MaxRetry int 字段
|
||
├── configs/config.yaml # [MODIFY] MattingModel、ClassificationModel 段各新增 MaxRetry: 3
|
||
├── configs/config.example.yaml # [MODIFY] MattingModel、ClassificationModel 段各新增 MaxRetry: 3
|
||
├── main.go # [MODIFY] 读取 MattingModel、ClassificationModel 后对 MaxRetry<=0 兜底为 3
|
||
└── internal/service/clothes.go # [MODIFY] 新增 retryCall 泛型辅助函数,抠图/分类调用接入重试
|
||
``` |