perf(backend): 使用 gjson/sjson 优化热路径 JSON 处理

将 API 网关热路径中的 json.Unmarshal+json.Marshal 替换为 gjson 零拷贝查询和 sjson 精准写入:
- unwrapV1InternalResponse 性能提升 22x(4009ns→182ns),内存分配减少 28.5x
- unwrapGeminiResponse、extractGeminiUsage、estimateGeminiCountTokens、ParseGeminiRateLimitResetTime 改为接收 []byte 使用 gjson 提取
- ParseGatewayRequest 的 model/stream/metadata/thinking/max_tokens 改用 gjson 类型安全提取
- Handler 层(sora/openai)改用 gjson 提取字段、sjson 注入/修改字段,移除 map[string]any 中间变量
- Sora Client 响应解析改用 gjson ForEach 遍历,减少内存分配
- 新增约 100 个单元测试用例,所有改动函数覆盖率 >85%

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
yangjianbo
2026-02-10 08:59:30 +08:00
parent 29ca1290b3
commit 58912d4ac5
14 changed files with 1686 additions and 324 deletions

View File

@@ -9,6 +9,7 @@ import (
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
@@ -889,3 +890,144 @@ func TestAntigravityClientWriter(t *testing.T) {
require.True(t, cw.Disconnected())
})
}
// TestUnwrapV1InternalResponse 测试 unwrapV1InternalResponse 的各种输入场景
func TestUnwrapV1InternalResponse(t *testing.T) {
svc := &AntigravityGatewayService{}
// 构造 >50KB 的大型 JSON
largePadding := strings.Repeat("x", 50*1024)
largeInput := []byte(fmt.Sprintf(`{"response":{"id":"big","pad":"%s"}}`, largePadding))
largeExpected := fmt.Sprintf(`{"id":"big","pad":"%s"}`, largePadding)
tests := []struct {
name string
input []byte
expected string
wantErr bool
}{
{
name: "正常 response 包装",
input: []byte(`{"response":{"id":"123","content":"hello"}}`),
expected: `{"id":"123","content":"hello"}`,
},
{
name: "无 response 透传",
input: []byte(`{"id":"456"}`),
expected: `{"id":"456"}`,
},
{
name: "空 JSON",
input: []byte(`{}`),
expected: `{}`,
},
{
name: "response 为 null",
input: []byte(`{"response":null}`),
expected: `null`,
},
{
name: "response 为基础类型 string",
input: []byte(`{"response":"hello"}`),
expected: `"hello"`,
},
{
name: "非法 JSON",
input: []byte(`not json`),
expected: `not json`,
},
{
name: "嵌套 response 只解一层",
input: []byte(`{"response":{"response":{"inner":true}}}`),
expected: `{"response":{"inner":true}}`,
},
{
name: "大型 JSON >50KB",
input: largeInput,
expected: largeExpected,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := svc.unwrapV1InternalResponse(tt.input)
if tt.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
require.Equal(t, tt.expected, strings.TrimSpace(string(got)))
})
}
}
// --- unwrapV1InternalResponse benchmark 对照组 ---
// unwrapV1InternalResponseOld 旧实现Unmarshal+Marshal 双重开销(仅用于 benchmark 对照)
func unwrapV1InternalResponseOld(body []byte) ([]byte, error) {
var outer map[string]any
if err := json.Unmarshal(body, &outer); err != nil {
return nil, err
}
if resp, ok := outer["response"]; ok {
return json.Marshal(resp)
}
return body, nil
}
func BenchmarkUnwrapV1Internal_Old_Small(b *testing.B) {
body := []byte(`{"response":{"candidates":[{"content":{"parts":[{"text":"hello world"}]}}],"usageMetadata":{"promptTokenCount":10,"candidatesTokenCount":5}}}`)
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = unwrapV1InternalResponseOld(body)
}
}
func BenchmarkUnwrapV1Internal_New_Small(b *testing.B) {
body := []byte(`{"response":{"candidates":[{"content":{"parts":[{"text":"hello world"}]}}],"usageMetadata":{"promptTokenCount":10,"candidatesTokenCount":5}}}`)
svc := &AntigravityGatewayService{}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = svc.unwrapV1InternalResponse(body)
}
}
func BenchmarkUnwrapV1Internal_Old_Large(b *testing.B) {
body := generateLargeUnwrapJSON(10 * 1024) // ~10KB
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = unwrapV1InternalResponseOld(body)
}
}
func BenchmarkUnwrapV1Internal_New_Large(b *testing.B) {
body := generateLargeUnwrapJSON(10 * 1024) // ~10KB
svc := &AntigravityGatewayService{}
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, _ = svc.unwrapV1InternalResponse(body)
}
}
// generateLargeUnwrapJSON 生成指定最小大小的包含 response 包装的 JSON
func generateLargeUnwrapJSON(minSize int) []byte {
parts := make([]map[string]string, 0)
current := 0
for current < minSize {
text := fmt.Sprintf("这是第 %d 段内容,用于填充 JSON 到目标大小。", len(parts)+1)
parts = append(parts, map[string]string{"text": text})
current += len(text) + 20 // 估算 JSON 编码开销
}
inner := map[string]any{
"candidates": []map[string]any{
{"content": map[string]any{"parts": parts}},
},
"usageMetadata": map[string]any{
"promptTokenCount": 100,
"candidatesTokenCount": 50,
},
}
outer := map[string]any{"response": inner}
b, _ := json.Marshal(outer)
return b
}