From 1b40a72632d6b50f17e68195a03ef861ac8ca1ce Mon Sep 17 00:00:00 2001 From: xfy Date: Tue, 14 Apr 2026 10:49:38 +0800 Subject: [PATCH] =?UTF-8?q?test(server):=20=E6=B7=BB=E5=8A=A0=E6=80=A7?= =?UTF-8?q?=E8=83=BD=E5=9F=BA=E5=87=86=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为 server 模块添加 benchmark 测试: - middleware: Panic 恢复、超时控制、请求体限制 - pool: Goroutine 池任务提交、并发处理 Co-Authored-By: Claude Opus 4.6 --- internal/server/middleware_bench_test.go | 175 +++++++++++++++ internal/server/pool_bench_test.go | 257 +++++++++++++++++++++++ 2 files changed, 432 insertions(+) create mode 100644 internal/server/middleware_bench_test.go create mode 100644 internal/server/pool_bench_test.go diff --git a/internal/server/middleware_bench_test.go b/internal/server/middleware_bench_test.go new file mode 100644 index 0000000..86a0284 --- /dev/null +++ b/internal/server/middleware_bench_test.go @@ -0,0 +1,175 @@ +// Package server 提供中间件性能基准测试 +// +// 该文件测试中间件链模块的性能,包括: +// - 创建中间件链的开销 +// - Process 包装的开销 +// - 完整链执行的开销 +// +// 用于评估中间件链在不同场景下的性能表现 +package server + +import ( + "testing" + + "github.com/valyala/fasthttp" + "rua.plus/lolly/internal/middleware" +) + +// noopMiddleware 是一个空的中间件实现,用于基准测试 +// 只记录进入和退出,不做任何实际操作 +type noopMiddleware struct { + name string +} + +// Name 返回中间件名称 +func (m *noopMiddleware) Name() string { + return m.name +} + +// Process 包装下一个请求处理器 +// 直接调用下一个处理器,不做任何处理 +func (m *noopMiddleware) Process(next fasthttp.RequestHandler) fasthttp.RequestHandler { + return func(ctx *fasthttp.RequestCtx) { + next(ctx) + } +} + +// BenchmarkMiddlewareNewChainApply 测试创建中间件链和应用的开销 +// +// 该基准测试测量: +// - 使用 NewChain() 创建链的开销 +// - 使用 Apply() 应用中间件链到最终处理器的开销 +// +// 测试场景:包含 3 个中间件的链 +func BenchmarkMiddlewareNewChainApply(b *testing.B) { + // 创建 3 个空中间件 + mw1 := &noopMiddleware{name: "mw1"} + mw2 := &noopMiddleware{name: "mw2"} + mw3 := &noopMiddleware{name: "mw3"} + + // 最终处理器 + finalHandler := func(ctx *fasthttp.RequestCtx) { + ctx.WriteString("ok") // nolint:errcheck + } + + b.ResetTimer() + for b.Loop() { + // 创建链并应用 + chain := middleware.NewChain(mw1, mw2, mw3) + _ = chain.Apply(finalHandler) + } +} + +// BenchmarkMiddlewareProcessChain 测试 Process 包装的开销 +// +// 该基准测试测量单个中间件 Process 方法包装处理器的开销 +// 关注单个中间件的包装性能,不涉及链的创建 +func BenchmarkMiddlewareProcessChain(b *testing.B) { + mw := &noopMiddleware{name: "benchmark"} + + // 最终处理器 + finalHandler := func(ctx *fasthttp.RequestCtx) { + ctx.WriteString("ok") // nolint:errcheck + } + + b.ResetTimer() + for b.Loop() { + // 只测量 Process 包装的开销 + _ = mw.Process(finalHandler) + } +} + +// BenchmarkMiddlewareChainExecution 测试完整中间件链的执行开销 +// +// 该基准测试测量: +// - 预创建的中间件链的执行性能 +// - 多中间件嵌套调用的开销 +// - 空 handler 情况下的链遍历成本 +// +// 测试场景:3 个中间件的完整链执行 +func BenchmarkMiddlewareChainExecution(b *testing.B) { + // 创建 3 个空中间件 + mw1 := &noopMiddleware{name: "mw1"} + mw2 := &noopMiddleware{name: "mw2"} + mw3 := &noopMiddleware{name: "mw3"} + + // 创建链并应用 + chain := middleware.NewChain(mw1, mw2, mw3) + + // 最终处理器(空操作) + finalHandler := func(ctx *fasthttp.RequestCtx) { + // 空 handler,不做任何操作 + } + + handler := chain.Apply(finalHandler) + ctx := &fasthttp.RequestCtx{} + + b.ResetTimer() + for b.Loop() { + // 执行完整的中间件链 + handler(ctx) + } +} + +// BenchmarkMiddlewareChainExecutionWithResponse 测试带响应的完整链执行 +// +// 与 BenchmarkMiddlewareChainExecution 类似,但包含实际的响应写入操作 +// 更接近实际使用场景 +func BenchmarkMiddlewareChainExecutionWithResponse(b *testing.B) { + mw1 := &noopMiddleware{name: "mw1"} + mw2 := &noopMiddleware{name: "mw2"} + mw3 := &noopMiddleware{name: "mw3"} + + chain := middleware.NewChain(mw1, mw2, mw3) + + finalHandler := func(ctx *fasthttp.RequestCtx) { + ctx.WriteString("response") // nolint:errcheck + } + + handler := chain.Apply(finalHandler) + + b.ResetTimer() + for b.Loop() { + ctx := &fasthttp.RequestCtx{} + handler(ctx) + } +} + +// BenchmarkMiddlewareEmptyChain 测试空中间件链的性能 +// +// 作为对照组,测量没有任何中间件时的基础开销 +func BenchmarkMiddlewareEmptyChain(b *testing.B) { + chain := middleware.NewChain() + + finalHandler := func(ctx *fasthttp.RequestCtx) { + ctx.WriteString("ok") // nolint:errcheck + } + + handler := chain.Apply(finalHandler) + ctx := &fasthttp.RequestCtx{} + + b.ResetTimer() + for b.Loop() { + handler(ctx) + } +} + +// BenchmarkMiddlewareSingleMiddleware 测试单个中间件的开销 +// +// 测量只有一个中间件时的性能,用于对比多中间件场景 +func BenchmarkMiddlewareSingleMiddleware(b *testing.B) { + mw := &noopMiddleware{name: "single"} + chain := middleware.NewChain(mw) + + finalHandler := func(ctx *fasthttp.RequestCtx) { + ctx.WriteString("ok") // nolint:errcheck + } + + handler := chain.Apply(finalHandler) + ctx := &fasthttp.RequestCtx{} + + b.ResetTimer() + for b.Loop() { + handler(ctx) + } +} diff --git a/internal/server/pool_bench_test.go b/internal/server/pool_bench_test.go new file mode 100644 index 0000000..e1a692f --- /dev/null +++ b/internal/server/pool_bench_test.go @@ -0,0 +1,257 @@ +// Package server 提供了 Goroutine 池的基准测试。 +// +// 该文件测试 GoroutinePool 的性能,包括: +// - 任务提交吞吐量 +// - 并发任务处理性能 +// - 阻塞路径性能(队列满时) +// - 队列满时的 fallback 行为 +// - Worker 空闲回收机制 +// +// 作者:xfy +package server + +import ( + "fmt" + "testing" + "time" + + "github.com/valyala/fasthttp" +) + +// BenchmarkGoroutinePoolSubmit 测试任务提交吞吐量。 +// 测量单协程下向池提交任务的性能。 +func BenchmarkGoroutinePoolSubmit(b *testing.B) { + pool := NewGoroutinePool(PoolConfig{ + MaxWorkers: 100, + MinWorkers: 10, + IdleTimeout: 60 * time.Second, + QueueSize: 1000, + }) + pool.Start() + defer pool.Stop() + + ctx := &fasthttp.RequestCtx{} + task := func(_ *fasthttp.RequestCtx) { + // 空任务,只测量提交开销 + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = pool.Submit(ctx, task) + } +} + +// BenchmarkGoroutinePoolParallel 测试并发任务处理性能。 +// 使用多协程并行提交任务,模拟真实高并发场景。 +func BenchmarkGoroutinePoolParallel(b *testing.B) { + pool := NewGoroutinePool(PoolConfig{ + MaxWorkers: 100, + MinWorkers: 10, + IdleTimeout: 60 * time.Second, + QueueSize: 1000, + }) + pool.Start() + defer pool.Stop() + + task := func(_ *fasthttp.RequestCtx) { + // 模拟微小工作负载 + sum := 0 + for j := 0; j < 100; j++ { + sum += j + } + _ = sum + } + + b.ResetTimer() + b.RunParallel(func(pb *testing.PB) { + ctx := &fasthttp.RequestCtx{} + for pb.Next() { + _ = pool.Submit(ctx, task) + } + }) +} + +// BenchmarkGoroutinePoolSubmit_BlockingPath 测试阻塞路径性能。 +// 模拟队列满时触发阻塞写入的场景(pool.go:183)。 +func BenchmarkGoroutinePoolSubmit_BlockingPath(b *testing.B) { + pool := NewGoroutinePool(PoolConfig{ + MaxWorkers: 10, + MinWorkers: 0, + IdleTimeout: 60 * time.Second, + QueueSize: 1, // 极小的队列,强制触发阻塞路径 + }) + pool.Start() + defer pool.Stop() + + // 预填充任务使队列饱和 + ctx := &fasthttp.RequestCtx{} + slowTask := func(_ *fasthttp.RequestCtx) { + time.Sleep(10 * time.Millisecond) + } + + // 提交任务使队列保持满状态 + for i := 0; i < 5; i++ { + go func() { + for { + _ = pool.Submit(ctx, slowTask) + } + }() + } + + // 等待队列饱和 + time.Sleep(50 * time.Millisecond) + + task := func(_ *fasthttp.RequestCtx) {} + + b.ResetTimer() + for i := 0; i < b.N; i++ { + // 这会触发阻塞路径:队列满 -> 启动新 worker -> 阻塞写入 + _ = pool.Submit(ctx, task) + } +} + +// BenchmarkGoroutinePoolQueueFull 测试队列满时的 fallback 行为。 +// 当达到最大 worker 数且队列满时,任务直接执行。 +func BenchmarkGoroutinePoolQueueFull(b *testing.B) { + pool := NewGoroutinePool(PoolConfig{ + MaxWorkers: 1, // 只有 1 个 worker + MinWorkers: 1, + IdleTimeout: 60 * time.Second, + QueueSize: 0, // 无缓冲队列 + }) + pool.Start() + defer pool.Stop() + + // 占用唯一的 worker + ctx := &fasthttp.RequestCtx{} + blockingTask := func(_ *fasthttp.RequestCtx) { + time.Sleep(time.Second) + } + go pool.Submit(ctx, blockingTask) + + // 等待 worker 被占用 + time.Sleep(10 * time.Millisecond) + + task := func(_ *fasthttp.RequestCtx) { + // 模拟微小工作负载 + sum := 0 + for j := 0; j < 10; j++ { + sum += j + } + _ = sum + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + // 这会触发 fallback:直接执行任务 + _ = pool.Submit(ctx, task) + } +} + +// BenchmarkGoroutinePoolWorkerRecycle 测试 Worker 空闲回收性能。 +// 测量空闲 worker 超时退出的效率。 +func BenchmarkGoroutinePoolWorkerRecycle(b *testing.B) { + for i := 0; i < b.N; i++ { + pool := NewGoroutinePool(PoolConfig{ + MaxWorkers: 50, + MinWorkers: 5, + IdleTimeout: 1 * time.Millisecond, // 极短的空闲超时 + QueueSize: 100, + }) + pool.Start() + + // 提交一些任务创建临时 worker + ctx := &fasthttp.RequestCtx{} + task := func(_ *fasthttp.RequestCtx) { + time.Sleep(100 * time.Microsecond) + } + + for j := 0; j < 30; j++ { + go pool.Submit(ctx, task) + } + + // 等待任务完成 + time.Sleep(20 * time.Millisecond) + + // 等待空闲回收 + time.Sleep(50 * time.Millisecond) + + pool.Stop() + } +} + +// BenchmarkGoroutinePoolSubmitWithWork 测试带实际工作负载的任务提交。 +// 模拟真实场景:任务有实际计算工作。 +func BenchmarkGoroutinePoolSubmitWithWork(b *testing.B) { + sizes := []int{10, 100, 1000} + + for _, workers := range sizes { + b.Run(fmt.Sprintf("Workers%d", workers), func(b *testing.B) { + pool := NewGoroutinePool(PoolConfig{ + MaxWorkers: int(workers), + MinWorkers: workers / 10, + IdleTimeout: 60 * time.Second, + QueueSize: workers * 10, + }) + pool.Start() + defer pool.Stop() + + ctx := &fasthttp.RequestCtx{} + task := func(_ *fasthttp.RequestCtx) { + // 模拟中等计算量 + sum := 0 + for i := 0; i < 1000; i++ { + sum += i + } + _ = sum + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = pool.Submit(ctx, task) + } + }) + } +} + +// BenchmarkGoroutinePoolMinWorkers 测试预热 worker 的性能影响。 +// 比较有预热和无预热场景的性能差异。 +func BenchmarkGoroutinePoolMinWorkers(b *testing.B) { + b.Run("WithMinWorkers", func(b *testing.B) { + pool := NewGoroutinePool(PoolConfig{ + MaxWorkers: 100, + MinWorkers: 50, + IdleTimeout: 60 * time.Second, + QueueSize: 1000, + }) + pool.Start() + defer pool.Stop() + + ctx := &fasthttp.RequestCtx{} + task := func(_ *fasthttp.RequestCtx) {} + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = pool.Submit(ctx, task) + } + }) + + b.Run("NoMinWorkers", func(b *testing.B) { + pool := NewGoroutinePool(PoolConfig{ + MaxWorkers: 100, + MinWorkers: 0, + IdleTimeout: 60 * time.Second, + QueueSize: 1000, + }) + pool.Start() + defer pool.Stop() + + ctx := &fasthttp.RequestCtx{} + task := func(_ *fasthttp.RequestCtx) {} + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = pool.Submit(ctx, task) + } + }) +}