feat(worker): 实现集群限流与自适应负载

保留平台模型 RPM、TPM 和并发策略语义,增加 PostgreSQL 集群级租约、饱和候选重选和多平台自动负载,避免突发任务固定等待首个平台。\n\n新增 Worker 实时负载采样、自适应 active/heavy 容量、心跳与管理端指标,并扩展本地 acceptance runner,覆盖三 Worker、同模型三平台 2/4/6 并发和 48 个带图视频突发任务。\n\n验证:go test ./...、go vet ./...、PostgreSQL 跨 Store 集成测试、gofmt、bash -n、ShellCheck 及本地集群 provider-burst 验收通过;48/48 成功,无越限、重复提交、重复计费、重复回调或终态资源泄漏。
This commit is contained in:
2026-08-03 00:13:46 +08:00
parent 9a01fd4657
commit c28bf74230
52 changed files with 3700 additions and 272 deletions
@@ -5,6 +5,7 @@ import "time"
type GeminiProfile struct {
Name string
Requests int
InputImages int
InputBytes int
OutputBytes int
DelayMin time.Duration
@@ -13,15 +14,19 @@ type GeminiProfile struct {
var (
GeminiBaseline = GeminiProfile{
Name: "gemini-baseline", Requests: 1000, InputBytes: 256 << 10, OutputBytes: 256 << 10,
Name: "gemini-baseline", Requests: 1000, InputImages: 1, InputBytes: 256 << 10, OutputBytes: 256 << 10,
DelayMin: 4 * time.Second, DelayMax: 4 * time.Second,
}
GeminiMultiImage = GeminiProfile{
Name: "gemini-multi-image", Requests: 192, InputImages: 3, InputBytes: 768 << 10, OutputBytes: 0,
DelayMin: 8 * time.Second, DelayMax: 15 * time.Second,
}
GeminiLarge = GeminiProfile{
Name: "gemini-large", Requests: 128, InputBytes: 2 << 20, OutputBytes: 4 << 20,
Name: "gemini-large", Requests: 128, InputImages: 1, InputBytes: 2 << 20, OutputBytes: 4 << 20,
DelayMin: 8 * time.Second, DelayMax: 15 * time.Second,
}
GeminiPeak = GeminiProfile{
Name: "gemini-peak", Requests: 32, InputBytes: 8 << 20, OutputBytes: 8 << 20,
Name: "gemini-peak", Requests: 32, InputImages: 1, InputBytes: 8 << 20, OutputBytes: 8 << 20,
DelayMin: 15 * time.Second, DelayMax: 30 * time.Second,
}
)
@@ -30,6 +35,8 @@ func GeminiProfileByName(name string) (GeminiProfile, bool) {
switch name {
case GeminiBaseline.Name:
return GeminiBaseline, true
case GeminiMultiImage.Name:
return GeminiMultiImage, true
case GeminiLarge.Name:
return GeminiLarge, true
case GeminiPeak.Name:
@@ -3,15 +3,18 @@ package acceptanceworkload
import "testing"
func TestGeminiProfilesAndVideoDistribution(t *testing.T) {
for _, profile := range []GeminiProfile{GeminiBaseline, GeminiLarge, GeminiPeak} {
for _, profile := range []GeminiProfile{GeminiBaseline, GeminiMultiImage, GeminiLarge, GeminiPeak} {
resolved, ok := GeminiProfileByName(profile.Name)
if !ok || resolved != profile {
t.Fatalf("profile %q resolved to %+v, ok=%v", profile.Name, resolved, ok)
}
if profile.Requests <= 0 || profile.InputBytes <= 0 || profile.OutputBytes <= 0 ||
if profile.Requests <= 0 || profile.InputImages <= 0 || profile.InputBytes < profile.InputImages || profile.OutputBytes < 0 ||
profile.DelayMin <= 0 || profile.DelayMax < profile.DelayMin {
t.Fatalf("invalid Gemini profile: %+v", profile)
}
if profile.Name != GeminiMultiImage.Name && profile.OutputBytes == 0 {
t.Fatalf("fixed-output Gemini profile has no output size: %+v", profile)
}
}
counts := map[int]int{}
for index := 0; index < 100; index++ {