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
+30 -8
View File
@@ -59,6 +59,7 @@ type Server struct {
type Report struct {
GeminiRequests int64 `json:"geminiRequests"`
GeminiInvalid int64 `json:"geminiInvalid"`
GeminiInputImages int64 `json:"geminiInputImages"`
GeminiInputBytes int64 `json:"geminiInputBytes"`
GeminiOutputBytes int64 `json:"geminiOutputBytes"`
VideoSubmissions int64 `json:"videoSubmissions"`
@@ -163,7 +164,7 @@ func (s *Server) geminiGenerateContent(w http.ResponseWriter, r *http.Request) {
writeProtocolError(w, http.StatusBadRequest, err.Error())
return
}
inputBytes, err := validateGeminiImageRequest(body)
inputBytes, inputImages, err := validateGeminiImageRequest(body)
if err != nil {
s.recordGeminiInvalid()
writeProtocolError(w, http.StatusBadRequest, err.Error())
@@ -180,6 +181,7 @@ func (s *Server) geminiGenerateContent(w http.ResponseWriter, r *http.Request) {
s.geminiIdempotency[idempotencyKey] = struct{}{}
}
s.report.GeminiRequests++
s.report.GeminiInputImages += int64(inputImages)
s.report.GeminiInputBytes += int64(inputBytes)
s.report.GeminiOutputBytes += int64(outputBytes)
s.mu.Unlock()
@@ -533,7 +535,7 @@ func decodeImageDataURL(raw string) ([]byte, error) {
return payload, nil
}
func validateGeminiImageRequest(body map[string]any) (int, error) {
func validateGeminiImageRequest(body map[string]any) (int, int, error) {
generationConfig, _ := body["generationConfig"].(map[string]any)
modalities, _ := generationConfig["responseModalities"].([]any)
hasImageModality := false
@@ -543,9 +545,10 @@ func validateGeminiImageRequest(body map[string]any) (int, error) {
}
}
if !hasImageModality {
return 0, errors.New("generationConfig.responseModalities must contain IMAGE")
return 0, 0, errors.New("generationConfig.responseModalities must contain IMAGE")
}
total := 0
images := 0
contents, _ := body["contents"].([]any)
for _, rawContent := range contents {
content, _ := rawContent.(map[string]any)
@@ -558,22 +561,41 @@ func validateGeminiImageRequest(body map[string]any) (int, error) {
}
encoded := strings.TrimSpace(stringValue(inline["data"]))
if encoded == "" {
fileData, _ := part["fileData"].(map[string]any)
if fileData == nil {
fileData, _ = part["file_data"].(map[string]any)
}
fileURI := strings.TrimSpace(stringValue(fileData["fileUri"]))
if fileURI == "" {
fileURI = strings.TrimSpace(stringValue(fileData["file_uri"]))
}
if fileURI == "" {
fileURI = strings.TrimSpace(stringValue(fileData["uri"]))
}
if fileURI == "" {
continue
}
if !strings.HasPrefix(fileURI, "http://") && !strings.HasPrefix(fileURI, "https://") {
return 0, 0, errors.New("Gemini fileData URI must use HTTP or HTTPS")
}
images++
continue
}
decoded, err := base64.StdEncoding.DecodeString(encoded)
if err != nil {
return 0, fmt.Errorf("Gemini inlineData is not valid Base64: %w", err)
return 0, 0, fmt.Errorf("Gemini inlineData is not valid Base64: %w", err)
}
if len(decoded) == 0 {
return 0, errors.New("Gemini inlineData is empty")
return 0, 0, errors.New("Gemini inlineData is empty")
}
total += len(decoded)
images++
}
}
if total == 0 {
return 0, errors.New("Gemini request has no inlineData image")
if images == 0 {
return 0, 0, errors.New("Gemini request has no inlineData or fileData image")
}
return total, nil
return total, images, nil
}
func (s *Server) recordGeminiInvalid() {