继续上一轮性能改造的剩余项: - 会话亲和表分 16 片(原来一个全局 mutex,每个请求都要走一遍), 键改为内联 FNV-1a 的 64 位摘要(原先是拼接字符串 + hex,每请求两次分配), 请求路径上零分配;新增键稳定性/无分配/分片并发测试。 - 记账落库按驱动自适应并发:Recorder 按用户分片到多个 worker(同一用户恒定 同片,流水顺序不变)。SQLite 只有一个写者,实测 4 worker 会互相抢锁把 p99 拖到 19–34ms,所以 SQLite 固定 1 个、PostgreSQL 用 4 个。 新增多分片落库与顺序测试(用显式 worker 数在 SQLite 上验证语义)。 - 访问日志:每请求一次 log.Printf 直写 stderr,高 QPS 下是系统调用热点; 改为 64 KiB 缓冲 + 每秒 flush(写/flush 互斥,-race 已验)。 - 原始体控量:LogRaw 落库的请求体截到 256 KiB、响应体截到 1 MiB 并标注原始 体积,避免单条日志几十 MB 拖慢 usage_logs 查询与备份。 - 渠道加权随机选路从 crypto/rand + big.Int 换成 math/rand/v2 的 IntN, 去掉每请求的系统调用与两次分配。 跳过的两项与原因: - 「PG 下把 model 目录查询合并成一次 JOIN」:目录已缓存 10s,冷路径每模型 10 秒才走一次,合并收益接近 0。 - 「每流一个 heartbeat goroutine」:Go 的 ticker 成本很低,千级并发流仍在 可接受范围,改动收益不抵风险。 实测(16 并发、mock 上游、SQLite):p50 679µs→558µs、p99 2.6ms→2.2ms、 RPS 18.7k→23.9k。验证:go vet、go test ./...、-race(proxy/channel/usage)全绿。
32 lines
905 B
Go
32 lines
905 B
Go
package proxy
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestCapRaw(t *testing.T) {
|
|
small := []byte("hello")
|
|
if got := capRaw(small, 10); got != "hello" {
|
|
t.Fatalf("未超限应原样返回,实际 %q", got)
|
|
}
|
|
big := []byte(strings.Repeat("x", 100))
|
|
got := capRaw(big, 10)
|
|
if !strings.HasPrefix(got, strings.Repeat("x", 10)) {
|
|
t.Fatalf("应保留前 10 字节:%q", got)
|
|
}
|
|
if !strings.Contains(got, "truncated by openteam: 100 bytes total") {
|
|
t.Fatalf("应标注被截断与原始大小:%q", got)
|
|
}
|
|
}
|
|
|
|
// 两个上限都远小于流式响应可能的体积,且响应上限比请求上限宽(响应通常是更大的那份)
|
|
func TestRawCapsSane(t *testing.T) {
|
|
if rawRequestCap <= 0 || rawResponseCap <= 0 {
|
|
t.Fatal("上限必须为正")
|
|
}
|
|
if rawRequestCap > rawResponseCap {
|
|
t.Fatalf("响应上限(%d)应不小于请求上限(%d)", rawResponseCap, rawRequestCap)
|
|
}
|
|
}
|