fix(worker): 限制大图归一化峰值内存

6K 多参考图在 P24 下会并发进入高内存缩放并击穿 2 GiB Worker。新增独立可配置的图片归一化并发,生产默认 2,只约束解码、缩放和重编码阶段,不占用视频上游等待槽;缩放器改为内存稳定的近似双线性实现。\n\n验证:6K 到 6000x2400 转换、信号量串行化、Go 全量测试、gofmt、Kubernetes 客户端 dry-run。
This commit is contained in:
2026-07-31 20:54:11 +08:00
parent f190af00be
commit 29533537ec
7 changed files with 127 additions and 9 deletions
@@ -14,6 +14,7 @@ import (
"net/http"
"os"
"strings"
"sync"
"time"
"github.com/easyai/easyai-ai-gateway/apps/api/internal/clients"
@@ -195,6 +196,11 @@ func (s *Service) normalizeVideoInputImageSource(ctx context.Context, source str
return source, false, original, original, nil
}
release, err := s.acquireImageNormalizationSlot(ctx)
if err != nil {
return "", false, original, image.Point{}, err
}
defer release()
decoded, _, err := image.Decode(bytes.NewReader(payload))
if err != nil {
return "", false, original, image.Point{}, fmt.Errorf("图片无法解码")
@@ -209,7 +215,7 @@ func (s *Service) normalizeVideoInputImageSource(ctx context.Context, source str
contentHeight := max(1, int(math.Round(float64(sourceBounds.Dy())*scale)))
left := (target.X - contentWidth) / 2
top := (target.Y - contentHeight) / 2
draw.CatmullRom.Scale(
draw.ApproxBiLinear.Scale(
canvas,
image.Rect(left, top, left+contentWidth, top+contentHeight),
decoded,
@@ -228,6 +234,21 @@ func (s *Service) normalizeVideoInputImageSource(ctx context.Context, source str
return "data:image/jpeg;base64," + base64.StdEncoding.EncodeToString(output.Bytes()), true, original, target, nil
}
func (s *Service) acquireImageNormalizationSlot(ctx context.Context) (func(), error) {
if s.imageNormalizeSlots == nil {
return func() {}, nil
}
select {
case s.imageNormalizeSlots <- struct{}{}:
var once sync.Once
return func() {
once.Do(func() { <-s.imageNormalizeSlots })
}, nil
case <-ctx.Done():
return nil, ctx.Err()
}
}
func (s *Service) readVideoInputImageBytes(ctx context.Context, source string) ([]byte, error) {
lower := strings.ToLower(source)
if strings.HasPrefix(lower, "data:") || (!strings.Contains(source, "://") && !strings.HasPrefix(source, "/")) {