test(benchmark): 新增组件级基准测试套件
- 新增 benchmark_context.go 标准化测试上下文构造器 - 新增静态文件处理器基准测试(缓存命中/未命中、try_files) - 新增访问日志中间件基准测试 - 新增压缩中间件基准测试(gzip/brotli、Pool 复用) - 新增限流器基准测试(令牌桶、滑动窗口、多客户端并发) - 新增变量展开基准测试(模板展开、Pool 操作)
This commit is contained in:
parent
f46b0dee07
commit
25bdba4e01
359
internal/benchmark/tools/benchmark_context.go
Normal file
359
internal/benchmark/tools/benchmark_context.go
Normal file
@ -0,0 +1,359 @@
|
||||
// Package tools 提供基准测试工具函数。
|
||||
//
|
||||
// 该文件提供标准化的基准测试上下文构造器,用于:
|
||||
// - 快速创建模拟的 fasthttp.RequestCtx
|
||||
// - 标准化测试数据大小
|
||||
// - 简化组件级基准测试编写
|
||||
//
|
||||
// 作者:xfy
|
||||
package tools
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
// BenchmarkContext 提供标准化的基准测试上下文。
|
||||
//
|
||||
// 包含构造模拟请求所需的所有配置。
|
||||
type BenchmarkContext struct {
|
||||
// RequestSize 请求数据大小
|
||||
RequestSize TestDataSize
|
||||
|
||||
// ResponseSize 响应数据大小
|
||||
ResponseSize TestDataSize
|
||||
|
||||
// Concurrency 并发级别(用于并行测试)
|
||||
Concurrency int
|
||||
|
||||
// randSrc 随机数生成器
|
||||
randSrc *rand.Rand
|
||||
}
|
||||
|
||||
// NewBenchmarkContext 创建基准测试上下文。
|
||||
//
|
||||
// 参数:
|
||||
// - reqSize: 请求数据大小
|
||||
// - respSize: 响应数据大小
|
||||
// - concurrency: 并发级别
|
||||
//
|
||||
// 返回值:
|
||||
// - *BenchmarkContext: 配置好的基准测试上下文
|
||||
func NewBenchmarkContext(reqSize, respSize TestDataSize, concurrency int) *BenchmarkContext {
|
||||
return &BenchmarkContext{
|
||||
RequestSize: reqSize,
|
||||
ResponseSize: respSize,
|
||||
Concurrency: concurrency,
|
||||
randSrc: rand.New(rand.NewSource(time.Now().UnixNano())),
|
||||
}
|
||||
}
|
||||
|
||||
// MockRequestCtx 构造模拟的 fasthttp.RequestCtx。
|
||||
//
|
||||
// 创建一个可用于基准测试的请求上下文,包含:
|
||||
// - 请求方法和路径
|
||||
// - 请求头
|
||||
// - 请求体
|
||||
// - 远程地址
|
||||
//
|
||||
// 参数:
|
||||
// - method: HTTP 方法 (GET, POST, etc.)
|
||||
// - path: 请求路径
|
||||
//
|
||||
// 返回值:
|
||||
// - *fasthttp.RequestCtx: 模拟的请求上下文
|
||||
func (bc *BenchmarkContext) MockRequestCtx(method, path string) *fasthttp.RequestCtx {
|
||||
ctx := &fasthttp.RequestCtx{}
|
||||
|
||||
// 设置请求行
|
||||
ctx.Request.Header.SetMethod(method)
|
||||
ctx.Request.SetRequestURI(path)
|
||||
|
||||
// 设置远程地址
|
||||
ctx.Init(&fasthttp.Request{}, &net.TCPAddr{
|
||||
IP: net.ParseIP("192.168.1.100"),
|
||||
Port: 12345,
|
||||
}, nil)
|
||||
|
||||
// 设置请求体
|
||||
if bc.RequestSize > 0 {
|
||||
body := GenerateTestData(bc.RequestSize)
|
||||
ctx.Request.SetBody(body)
|
||||
}
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
// MockRequestCtxWithHeaders 构造带自定义请求头的模拟上下文。
|
||||
//
|
||||
// 参数:
|
||||
// - method: HTTP 方法
|
||||
// - path: 请求路径
|
||||
// - headers: 请求头键值对
|
||||
//
|
||||
// 返回值:
|
||||
// - *fasthttp.RequestCtx: 模拟的请求上下文
|
||||
func (bc *BenchmarkContext) MockRequestCtxWithHeaders(method, path string, headers map[string]string) *fasthttp.RequestCtx {
|
||||
ctx := bc.MockRequestCtx(method, path)
|
||||
|
||||
// 设置请求头
|
||||
for key, value := range headers {
|
||||
ctx.Request.Header.Set(key, value)
|
||||
}
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
// MockRequestCtxWithBody 构造带自定义请求体的模拟上下文。
|
||||
//
|
||||
// 参数:
|
||||
// - method: HTTP 方法
|
||||
// - path: 请求路径
|
||||
// - body: 请求体内容
|
||||
//
|
||||
// 返回值:
|
||||
// - *fasthttp.RequestCtx: 模拟的请求上下文
|
||||
func (bc *BenchmarkContext) MockRequestCtxWithBody(method, path string, body []byte) *fasthttp.RequestCtx {
|
||||
ctx := bc.MockRequestCtx(method, path)
|
||||
ctx.Request.SetBody(body)
|
||||
return ctx
|
||||
}
|
||||
|
||||
// MockRequestCtxWithIP 构造带指定 IP 的模拟上下文。
|
||||
//
|
||||
// 参数:
|
||||
// - method: HTTP 方法
|
||||
// - path: 请求路径
|
||||
// - ip: 客户端 IP 地址
|
||||
//
|
||||
// 返回值:
|
||||
// - *fasthttp.RequestCtx: 模拟的请求上下文
|
||||
func (bc *BenchmarkContext) MockRequestCtxWithIP(method, path, ip string) *fasthttp.RequestCtx {
|
||||
ctx := &fasthttp.RequestCtx{}
|
||||
|
||||
ctx.Request.Header.SetMethod(method)
|
||||
ctx.Request.SetRequestURI(path)
|
||||
|
||||
ctx.Init(&fasthttp.Request{}, &net.TCPAddr{
|
||||
IP: net.ParseIP(ip),
|
||||
Port: 12345,
|
||||
}, nil)
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
// MockResponse 构造模拟响应数据。
|
||||
//
|
||||
// 根据 ResponseSize 生成响应体。
|
||||
//
|
||||
// 返回值:
|
||||
// - []byte: 模拟的响应数据
|
||||
func (bc *BenchmarkContext) MockResponse() []byte {
|
||||
return GenerateTestData(bc.ResponseSize)
|
||||
}
|
||||
|
||||
// MockResponseWithContentType 构造带 Content-Type 的模拟响应。
|
||||
//
|
||||
// 参数:
|
||||
// - statusCode: HTTP 状态码
|
||||
// - contentType: 内容类型
|
||||
//
|
||||
// 返回值:
|
||||
// - []byte: 模拟的响应数据
|
||||
func (bc *BenchmarkContext) MockResponseWithContentType(statusCode int, contentType string) []byte {
|
||||
// 返回响应体,调用者负责设置状态码和 Content-Type
|
||||
return GenerateTestData(bc.ResponseSize)
|
||||
}
|
||||
|
||||
// RandomIP 生成随机 IP 地址。
|
||||
//
|
||||
// 用于测试多客户端场景。
|
||||
//
|
||||
// 返回值:
|
||||
// - string: 随机 IP 地址
|
||||
func (bc *BenchmarkContext) RandomIP() string {
|
||||
return "192.168." + strconv.Itoa(bc.randSrc.Intn(256)) + "." + strconv.Itoa(bc.randSrc.Intn(256))
|
||||
}
|
||||
|
||||
// RandomPath 生成随机请求路径。
|
||||
//
|
||||
// 参数:
|
||||
// - prefix: 路径前缀
|
||||
//
|
||||
// 返回值:
|
||||
// - string: 随机路径
|
||||
func (bc *BenchmarkContext) RandomPath(prefix string) string {
|
||||
return prefix + "/" + strconv.Itoa(bc.randSrc.Intn(10000))
|
||||
}
|
||||
|
||||
// MockJSONRequest 构造 JSON 格式的模拟请求。
|
||||
//
|
||||
// 参数:
|
||||
// - path: 请求路径
|
||||
// - data: JSON 数据
|
||||
//
|
||||
// 返回值:
|
||||
// - *fasthttp.RequestCtx: 模拟的请求上下文
|
||||
func (bc *BenchmarkContext) MockJSONRequest(path string, data []byte) *fasthttp.RequestCtx {
|
||||
ctx := bc.MockRequestCtx("POST", path)
|
||||
ctx.Request.Header.SetContentType("application/json")
|
||||
ctx.Request.SetBody(data)
|
||||
return ctx
|
||||
}
|
||||
|
||||
// MockFormRequest 构造表单格式的模拟请求。
|
||||
//
|
||||
// 参数:
|
||||
// - path: 请求路径
|
||||
// - form: 表单数据键值对
|
||||
//
|
||||
// 返回值:
|
||||
// - *fasthttp.RequestCtx: 模拟的请求上下文
|
||||
func (bc *BenchmarkContext) MockFormRequest(path string, form map[string]string) *fasthttp.RequestCtx {
|
||||
ctx := bc.MockRequestCtx("POST", path)
|
||||
ctx.Request.Header.SetContentType("application/x-www-form-urlencoded")
|
||||
|
||||
// 构造表单数据
|
||||
var buf bytes.Buffer
|
||||
first := true
|
||||
for key, value := range form {
|
||||
if !first {
|
||||
buf.WriteByte('&')
|
||||
}
|
||||
buf.WriteString(key)
|
||||
buf.WriteByte('=')
|
||||
buf.WriteString(value)
|
||||
first = false
|
||||
}
|
||||
ctx.Request.SetBody(buf.Bytes())
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
||||
// SetResponse 设置模拟上下文的响应。
|
||||
//
|
||||
// 用于测试响应处理逻辑。
|
||||
//
|
||||
// 参数:
|
||||
// - ctx: 请求上下文
|
||||
// - statusCode: HTTP 状态码
|
||||
// - body: 响应体
|
||||
func (bc *BenchmarkContext) SetResponse(ctx *fasthttp.RequestCtx, statusCode int, body []byte) {
|
||||
ctx.Response.SetStatusCode(statusCode)
|
||||
ctx.Response.SetBody(body)
|
||||
}
|
||||
|
||||
// SetJSONResponse 设置 JSON 格式的模拟响应。
|
||||
//
|
||||
// 参数:
|
||||
// - ctx: 请求上下文
|
||||
// - statusCode: HTTP 状态码
|
||||
// - json: JSON 数据
|
||||
func (bc *BenchmarkContext) SetJSONResponse(ctx *fasthttp.RequestCtx, statusCode int, json []byte) {
|
||||
ctx.Response.SetStatusCode(statusCode)
|
||||
ctx.Response.Header.SetContentType("application/json")
|
||||
ctx.Response.SetBody(json)
|
||||
}
|
||||
|
||||
// DefaultBenchmarkContext 返回默认的基准测试上下文。
|
||||
//
|
||||
// 配置:
|
||||
// - RequestSize: 1KB
|
||||
// - ResponseSize: 1KB
|
||||
// - Concurrency: 1
|
||||
//
|
||||
// 返回值:
|
||||
// - *BenchmarkContext: 默认配置的上下文
|
||||
func DefaultBenchmarkContext() *BenchmarkContext {
|
||||
return NewBenchmarkContext(Size1KB, Size1KB, 1)
|
||||
}
|
||||
|
||||
// SmallRequestContext 返回小请求的基准测试上下文。
|
||||
//
|
||||
// 配置:
|
||||
// - RequestSize: 100B
|
||||
// - ResponseSize: 100B
|
||||
//
|
||||
// 返回值:
|
||||
// - *BenchmarkContext: 小请求配置的上下文
|
||||
func SmallRequestContext() *BenchmarkContext {
|
||||
return NewBenchmarkContext(100, 100, 1)
|
||||
}
|
||||
|
||||
// LargeRequestContext 返回大请求的基准测试上下文。
|
||||
//
|
||||
// 配置:
|
||||
// - RequestSize: 100KB
|
||||
// - ResponseSize: 100KB
|
||||
//
|
||||
// 返回值:
|
||||
// - *BenchmarkContext: 大请求配置的上下文
|
||||
func LargeRequestContext() *BenchmarkContext {
|
||||
return NewBenchmarkContext(Size100KB, Size100KB, 1)
|
||||
}
|
||||
|
||||
// HighConcurrencyContext 返回高并发基准测试上下文。
|
||||
//
|
||||
// 配置:
|
||||
// - Concurrency: 100
|
||||
//
|
||||
// 返回值:
|
||||
// - *BenchmarkContext: 高并发配置的上下文
|
||||
func HighConcurrencyContext() *BenchmarkContext {
|
||||
return NewBenchmarkContext(Size1KB, Size1KB, 100)
|
||||
}
|
||||
|
||||
// BenchmarkContextPool 提供基准测试上下文的池。
|
||||
//
|
||||
// 用于减少内存分配开销。
|
||||
type BenchmarkContextPool struct {
|
||||
pool chan *BenchmarkContext
|
||||
}
|
||||
|
||||
// NewBenchmarkContextPool 创建基准测试上下文池。
|
||||
//
|
||||
// 参数:
|
||||
// - size: 池大小
|
||||
//
|
||||
// 返回值:
|
||||
// - *BenchmarkContextPool: 上下文池
|
||||
func NewBenchmarkContextPool(size int) *BenchmarkContextPool {
|
||||
p := &BenchmarkContextPool{
|
||||
pool: make(chan *BenchmarkContext, size),
|
||||
}
|
||||
// 预填充池
|
||||
for i := 0; i < size; i++ {
|
||||
p.pool <- DefaultBenchmarkContext()
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// Get 从池中获取基准测试上下文。
|
||||
//
|
||||
// 返回值:
|
||||
// - *BenchmarkContext: 基准测试上下文
|
||||
func (p *BenchmarkContextPool) Get() *BenchmarkContext {
|
||||
select {
|
||||
case ctx := <-p.pool:
|
||||
return ctx
|
||||
default:
|
||||
return DefaultBenchmarkContext()
|
||||
}
|
||||
}
|
||||
|
||||
// Put 将基准测试上下文放回池中。
|
||||
//
|
||||
// 参数:
|
||||
// - ctx: 基准测试上下文
|
||||
func (p *BenchmarkContextPool) Put(ctx *BenchmarkContext) {
|
||||
select {
|
||||
case p.pool <- ctx:
|
||||
default:
|
||||
// 池已满,丢弃
|
||||
}
|
||||
}
|
||||
213
internal/handler/static_bench_test.go
Normal file
213
internal/handler/static_bench_test.go
Normal file
@ -0,0 +1,213 @@
|
||||
// Package handler 提供静态文件处理器的基准测试。
|
||||
//
|
||||
// 该文件测试文件查找、缓存命中/未命中、try_files 等场景的性能。
|
||||
//
|
||||
// 作者:xfy
|
||||
package handler
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
"rua.plus/lolly/internal/cache"
|
||||
)
|
||||
|
||||
// setupStaticTestDir 创建临时测试目录。
|
||||
func setupStaticTestDir() (string, func()) {
|
||||
dir, err := os.MkdirTemp("", "static_bench_*")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 创建测试文件
|
||||
testFiles := map[string][]byte{
|
||||
"index.html": []byte("<html><body>Index</body></html>"),
|
||||
"style.css": make([]byte, 1024), // 1KB
|
||||
"large.json": make([]byte, 10*1024), // 10KB
|
||||
"nested/file.js": make([]byte, 5*1024), // 5KB
|
||||
}
|
||||
|
||||
for path, content := range testFiles {
|
||||
fullPath := filepath.Join(dir, path)
|
||||
if err := os.MkdirAll(filepath.Dir(fullPath), 0755); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := os.WriteFile(fullPath, content, 0644); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
cleanup := func() {
|
||||
os.RemoveAll(dir)
|
||||
}
|
||||
|
||||
return dir, cleanup
|
||||
}
|
||||
|
||||
// BenchmarkStaticFileLookup 测试文件路径查找性能。
|
||||
func BenchmarkStaticFileLookup(b *testing.B) {
|
||||
dir, cleanup := setupStaticTestDir()
|
||||
defer cleanup()
|
||||
|
||||
handler := NewStaticHandler(dir, "/", []string{"index.html"}, false)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
ctx := &fasthttp.RequestCtx{}
|
||||
ctx.Request.SetRequestURI("/style.css")
|
||||
handler.Handle(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkStaticFileCacheHit 测试缓存命中场景性能。
|
||||
func BenchmarkStaticFileCacheHit(b *testing.B) {
|
||||
dir, cleanup := setupStaticTestDir()
|
||||
defer cleanup()
|
||||
|
||||
fc := cache.NewFileCache(1000, 10*1024*1024, 0) // 1000 文件或 10MB
|
||||
handler := NewStaticHandler(dir, "/", []string{"index.html"}, false)
|
||||
handler.SetFileCache(fc)
|
||||
|
||||
// 预热缓存
|
||||
ctx := &fasthttp.RequestCtx{}
|
||||
ctx.Request.SetRequestURI("/style.css")
|
||||
handler.Handle(ctx)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
ctx := &fasthttp.RequestCtx{}
|
||||
ctx.Request.SetRequestURI("/style.css")
|
||||
handler.Handle(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkStaticFileCacheMiss_1KB 测试 1KB 文件缓存未命中场景性能。
|
||||
func BenchmarkStaticFileCacheMiss_1KB(b *testing.B) {
|
||||
dir, cleanup := setupStaticTestDir()
|
||||
defer cleanup()
|
||||
|
||||
handler := NewStaticHandler(dir, "/", []string{"index.html"}, false)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
ctx := &fasthttp.RequestCtx{}
|
||||
ctx.Request.SetRequestURI("/style.css")
|
||||
handler.Handle(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkStaticFileCacheMiss_10KB 测试 10KB 文件缓存未命中场景性能。
|
||||
func BenchmarkStaticFileCacheMiss_10KB(b *testing.B) {
|
||||
dir, cleanup := setupStaticTestDir()
|
||||
defer cleanup()
|
||||
|
||||
handler := NewStaticHandler(dir, "/", []string{"index.html"}, false)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
ctx := &fasthttp.RequestCtx{}
|
||||
ctx.Request.SetRequestURI("/large.json")
|
||||
handler.Handle(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkStaticTryFiles 测试 try_files 查找性能。
|
||||
func BenchmarkStaticTryFiles(b *testing.B) {
|
||||
dir, cleanup := setupStaticTestDir()
|
||||
defer cleanup()
|
||||
|
||||
handler := NewStaticHandler(dir, "/", []string{"index.html"}, false)
|
||||
handler.SetTryFiles([]string{"$uri", "$uri/", "/index.html"}, false, nil)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
ctx := &fasthttp.RequestCtx{}
|
||||
ctx.Request.SetRequestURI("/nonexistent/path")
|
||||
handler.Handle(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkStaticIndex 测试索引文件查找性能。
|
||||
func BenchmarkStaticIndex(b *testing.B) {
|
||||
dir, cleanup := setupStaticTestDir()
|
||||
defer cleanup()
|
||||
|
||||
handler := NewStaticHandler(dir, "/", []string{"index.html", "index.htm"}, false)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
ctx := &fasthttp.RequestCtx{}
|
||||
ctx.Request.SetRequestURI("/")
|
||||
handler.Handle(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkStaticNestedFile 测试嵌套文件查找性能。
|
||||
func BenchmarkStaticNestedFile(b *testing.B) {
|
||||
dir, cleanup := setupStaticTestDir()
|
||||
defer cleanup()
|
||||
|
||||
handler := NewStaticHandler(dir, "/", []string{"index.html"}, false)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
ctx := &fasthttp.RequestCtx{}
|
||||
ctx.Request.SetRequestURI("/nested/file.js")
|
||||
handler.Handle(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkStaticFileNotFound 测试文件未找到场景性能。
|
||||
func BenchmarkStaticFileNotFound(b *testing.B) {
|
||||
dir, cleanup := setupStaticTestDir()
|
||||
defer cleanup()
|
||||
|
||||
handler := NewStaticHandler(dir, "/", []string{"index.html"}, false)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
ctx := &fasthttp.RequestCtx{}
|
||||
ctx.Request.SetRequestURI("/nonexistent/file.txt")
|
||||
handler.Handle(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkStaticWithCacheParallel 测试带缓存的并发访问性能。
|
||||
func BenchmarkStaticWithCacheParallel(b *testing.B) {
|
||||
dir, cleanup := setupStaticTestDir()
|
||||
defer cleanup()
|
||||
|
||||
fc := cache.NewFileCache(1000, 10*1024*1024, 0)
|
||||
handler := NewStaticHandler(dir, "/", []string{"index.html"}, false)
|
||||
handler.SetFileCache(fc)
|
||||
|
||||
paths := []string{"/style.css", "/large.json", "/nested/file.js", "/index.html"}
|
||||
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
ctx := &fasthttp.RequestCtx{}
|
||||
ctx.Request.SetRequestURI(paths[i%len(paths)])
|
||||
handler.Handle(ctx)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// BenchmarkStaticFileLookupWithAlias 测试 alias 模式下的文件查找性能。
|
||||
func BenchmarkStaticFileLookupWithAlias(b *testing.B) {
|
||||
dir, cleanup := setupStaticTestDir()
|
||||
defer cleanup()
|
||||
|
||||
handler := NewStaticHandlerWithAlias(dir+"/", "/static/", []string{"index.html"}, false)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
ctx := &fasthttp.RequestCtx{}
|
||||
ctx.Request.SetRequestURI("/static/style.css")
|
||||
handler.Handle(ctx)
|
||||
}
|
||||
}
|
||||
70
internal/middleware/accesslog/accesslog_bench_test.go
Normal file
70
internal/middleware/accesslog/accesslog_bench_test.go
Normal file
@ -0,0 +1,70 @@
|
||||
// Package accesslog 提供访问日志中间件的基准测试。
|
||||
//
|
||||
// 该文件测试日志记录的性能开销。
|
||||
//
|
||||
// 作者:xfy
|
||||
package accesslog
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
"rua.plus/lolly/internal/config"
|
||||
)
|
||||
|
||||
// BenchmarkAccessLogProcess 测试访问日志中间件处理性能。
|
||||
func BenchmarkAccessLogProcess(b *testing.B) {
|
||||
cfg := &config.LoggingConfig{
|
||||
Access: config.AccessLogConfig{
|
||||
Path: "/dev/null",
|
||||
Format: "combined",
|
||||
},
|
||||
}
|
||||
al := New(cfg)
|
||||
defer al.Close()
|
||||
|
||||
mockHandler := func(ctx *fasthttp.RequestCtx) {
|
||||
ctx.SetStatusCode(fasthttp.StatusOK)
|
||||
ctx.SetBodyString("Hello, World!")
|
||||
}
|
||||
|
||||
handler := al.Process(mockHandler)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
ctx := &fasthttp.RequestCtx{}
|
||||
ctx.Request.Header.SetMethod(fasthttp.MethodGet)
|
||||
ctx.Request.SetRequestURI("/api/test")
|
||||
ctx.Request.Header.SetHost("example.com")
|
||||
handler(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkAccessLogProcessParallel 测试并发场景下的访问日志性能。
|
||||
func BenchmarkAccessLogProcessParallel(b *testing.B) {
|
||||
cfg := &config.LoggingConfig{
|
||||
Access: config.AccessLogConfig{
|
||||
Path: "/dev/null",
|
||||
Format: "combined",
|
||||
},
|
||||
}
|
||||
al := New(cfg)
|
||||
defer al.Close()
|
||||
|
||||
mockHandler := func(ctx *fasthttp.RequestCtx) {
|
||||
ctx.SetStatusCode(fasthttp.StatusOK)
|
||||
ctx.SetBodyString("OK")
|
||||
}
|
||||
|
||||
handler := al.Process(mockHandler)
|
||||
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
ctx := &fasthttp.RequestCtx{}
|
||||
ctx.Request.Header.SetMethod(fasthttp.MethodGet)
|
||||
ctx.Request.SetRequestURI("/api/test")
|
||||
handler(ctx)
|
||||
}
|
||||
})
|
||||
}
|
||||
278
internal/middleware/compression/compression_bench_test.go
Normal file
278
internal/middleware/compression/compression_bench_test.go
Normal file
@ -0,0 +1,278 @@
|
||||
// Package compression 提供压缩中间件的基准测试。
|
||||
//
|
||||
// 该文件测试 gzip/brotli 压缩性能、Pool 复用效率和组件级性能。
|
||||
//
|
||||
// 作者:xfy
|
||||
package compression
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
"rua.plus/lolly/internal/benchmark/tools"
|
||||
"rua.plus/lolly/internal/config"
|
||||
)
|
||||
|
||||
// BenchmarkGzipCompress_1KB 测试 gzip 压缩 1KB 数据的性能。
|
||||
func BenchmarkGzipCompress_1KB(b *testing.B) {
|
||||
cfg := &config.CompressionConfig{
|
||||
Type: "gzip",
|
||||
Level: 6,
|
||||
MinSize: 100,
|
||||
Types: []string{"text/html", "application/json"},
|
||||
}
|
||||
mw, _ := New(cfg)
|
||||
|
||||
data := tools.GenerateTestData(tools.Size1KB)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
mw.compressGzip(data)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkGzipCompress_10KB 测试 gzip 压缩 10KB 数据的性能。
|
||||
func BenchmarkGzipCompress_10KB(b *testing.B) {
|
||||
cfg := &config.CompressionConfig{
|
||||
Type: "gzip",
|
||||
Level: 6,
|
||||
MinSize: 100,
|
||||
}
|
||||
mw, _ := New(cfg)
|
||||
|
||||
data := tools.GenerateTestData(tools.Size10KB)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
mw.compressGzip(data)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkGzipCompress_100KB 测试 gzip 压缩 100KB 数据的性能。
|
||||
func BenchmarkGzipCompress_100KB(b *testing.B) {
|
||||
cfg := &config.CompressionConfig{
|
||||
Type: "gzip",
|
||||
Level: 6,
|
||||
MinSize: 100,
|
||||
}
|
||||
mw, _ := New(cfg)
|
||||
|
||||
data := tools.GenerateTestData(tools.Size100KB)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
mw.compressGzip(data)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkBrotliCompress_1KB 测试 brotli 压缩 1KB 数据的性能。
|
||||
func BenchmarkBrotliCompress_1KB(b *testing.B) {
|
||||
cfg := &config.CompressionConfig{
|
||||
Type: "brotli",
|
||||
Level: 6,
|
||||
MinSize: 100,
|
||||
}
|
||||
mw, _ := New(cfg)
|
||||
|
||||
data := tools.GenerateTestData(tools.Size1KB)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
mw.compressBrotli(data)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkBrotliCompress_10KB 测试 brotli 压缩 10KB 数据的性能。
|
||||
func BenchmarkBrotliCompress_10KB(b *testing.B) {
|
||||
cfg := &config.CompressionConfig{
|
||||
Type: "brotli",
|
||||
Level: 6,
|
||||
MinSize: 100,
|
||||
}
|
||||
mw, _ := New(cfg)
|
||||
|
||||
data := tools.GenerateTestData(tools.Size10KB)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
mw.compressBrotli(data)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkCompressionPool 测试压缩 Pool 复用效率。
|
||||
//
|
||||
// 模拟实际使用场景,反复从 Pool 获取和归还压缩器。
|
||||
func BenchmarkCompressionPool(b *testing.B) {
|
||||
cfg := &config.CompressionConfig{
|
||||
Type: "gzip",
|
||||
Level: 6,
|
||||
MinSize: 100,
|
||||
}
|
||||
mw, _ := New(cfg)
|
||||
|
||||
data := tools.GenerateTestData(tools.Size1KB)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
mw.compressGzip(data)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkCompressionMiddleware 组件级测试:测量压缩中间件本身的开销。
|
||||
//
|
||||
// 使用 mockHandler 排除下游处理的影响。
|
||||
func BenchmarkCompressionMiddleware(b *testing.B) {
|
||||
cfg := &config.CompressionConfig{
|
||||
Type: "gzip",
|
||||
Level: 6,
|
||||
MinSize: 100,
|
||||
Types: []string{
|
||||
"text/html", "text/css", "application/json",
|
||||
},
|
||||
}
|
||||
mw, _ := New(cfg)
|
||||
|
||||
// 创建 10KB 响应数据
|
||||
responseBody := tools.GenerateTestData(tools.Size10KB)
|
||||
|
||||
// Mock handler 返回固定响应
|
||||
mockHandler := func(ctx *fasthttp.RequestCtx) {
|
||||
ctx.SetStatusCode(fasthttp.StatusOK)
|
||||
ctx.SetContentType("application/json")
|
||||
ctx.SetBody(responseBody)
|
||||
}
|
||||
|
||||
handler := mw.Process(mockHandler)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
ctx := &fasthttp.RequestCtx{}
|
||||
ctx.Request.Header.SetMethod(fasthttp.MethodGet)
|
||||
ctx.Request.SetRequestURI("/api/test")
|
||||
ctx.Request.Header.Set("Accept-Encoding", "gzip")
|
||||
handler(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkCompressionMiddlewareNoCompress 测试无需压缩场景的性能开销。
|
||||
//
|
||||
// 当客户端不支持压缩时,中间件应几乎无额外开销。
|
||||
func BenchmarkCompressionMiddlewareNoCompress(b *testing.B) {
|
||||
cfg := &config.CompressionConfig{
|
||||
Type: "gzip",
|
||||
Level: 6,
|
||||
MinSize: 100,
|
||||
}
|
||||
mw, _ := New(cfg)
|
||||
|
||||
responseBody := tools.GenerateTestData(tools.Size10KB)
|
||||
|
||||
mockHandler := func(ctx *fasthttp.RequestCtx) {
|
||||
ctx.SetStatusCode(fasthttp.StatusOK)
|
||||
ctx.SetContentType("application/json")
|
||||
ctx.SetBody(responseBody)
|
||||
}
|
||||
|
||||
handler := mw.Process(mockHandler)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
ctx := &fasthttp.RequestCtx{}
|
||||
ctx.Request.Header.SetMethod(fasthttp.MethodGet)
|
||||
ctx.Request.SetRequestURI("/api/test")
|
||||
// 不设置 Accept-Encoding 头
|
||||
handler(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkIsCompressible 测试 MIME 类型检查性能。
|
||||
func BenchmarkIsCompressible(b *testing.B) {
|
||||
cfg := &config.CompressionConfig{
|
||||
Type: "gzip",
|
||||
Level: 6,
|
||||
Types: []string{
|
||||
"text/html", "text/css", "text/javascript",
|
||||
"application/json", "application/javascript",
|
||||
},
|
||||
}
|
||||
mw, _ := New(cfg)
|
||||
|
||||
contentTypes := []string{
|
||||
"application/json",
|
||||
"text/html; charset=utf-8",
|
||||
"image/png",
|
||||
"application/octet-stream",
|
||||
"text/css",
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, ct := range contentTypes {
|
||||
mw.isCompressible(ct)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkCompressionLevelComparison 比较不同压缩级别的性能。
|
||||
func BenchmarkCompressionLevelComparison(b *testing.B) {
|
||||
data := tools.GenerateTestData(tools.Size10KB)
|
||||
|
||||
b.Run("Level1", func(b *testing.B) {
|
||||
cfg := &config.CompressionConfig{Type: "gzip", Level: 1}
|
||||
mw, _ := New(cfg)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
mw.compressGzip(data)
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("Level6", func(b *testing.B) {
|
||||
cfg := &config.CompressionConfig{Type: "gzip", Level: 6}
|
||||
mw, _ := New(cfg)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
mw.compressGzip(data)
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("Level9", func(b *testing.B) {
|
||||
cfg := &config.CompressionConfig{Type: "gzip", Level: 9}
|
||||
mw, _ := New(cfg)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
mw.compressGzip(data)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// BenchmarkCompressionMiddlewareParallel 测试并发场景下的中间件性能。
|
||||
func BenchmarkCompressionMiddlewareParallel(b *testing.B) {
|
||||
cfg := &config.CompressionConfig{
|
||||
Type: "gzip",
|
||||
Level: 6,
|
||||
MinSize: 100,
|
||||
Types: []string{"application/json"},
|
||||
}
|
||||
mw, _ := New(cfg)
|
||||
|
||||
responseBody := tools.GenerateTestData(tools.Size10KB)
|
||||
|
||||
mockHandler := func(ctx *fasthttp.RequestCtx) {
|
||||
ctx.SetStatusCode(fasthttp.StatusOK)
|
||||
ctx.SetContentType("application/json")
|
||||
ctx.SetBody(responseBody)
|
||||
}
|
||||
|
||||
handler := mw.Process(mockHandler)
|
||||
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
ctx := &fasthttp.RequestCtx{}
|
||||
ctx.Request.Header.SetMethod(fasthttp.MethodGet)
|
||||
ctx.Request.SetRequestURI("/api/test")
|
||||
ctx.Request.Header.Set("Accept-Encoding", "gzip")
|
||||
handler(ctx)
|
||||
}
|
||||
})
|
||||
}
|
||||
228
internal/middleware/security/ratelimit_bench_test.go
Normal file
228
internal/middleware/security/ratelimit_bench_test.go
Normal file
@ -0,0 +1,228 @@
|
||||
// Package security 提供限流中间件的基准测试。
|
||||
//
|
||||
// 该文件测试令牌桶限流器的性能,包括单客户端和多客户端场景。
|
||||
//
|
||||
// 作者:xfy
|
||||
package security
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
"rua.plus/lolly/internal/config"
|
||||
)
|
||||
|
||||
// setupRateLimitRequestCtx 创建用于基准测试的 fasthttp.RequestCtx。
|
||||
func setupRateLimitRequestCtx(ip string) *fasthttp.RequestCtx {
|
||||
ctx := &fasthttp.RequestCtx{}
|
||||
ctx.Request.Header.SetMethod(fasthttp.MethodGet)
|
||||
ctx.Request.SetRequestURI("/api/test")
|
||||
ctx.Init(&fasthttp.Request{}, &net.TCPAddr{
|
||||
IP: net.ParseIP(ip),
|
||||
Port: 12345,
|
||||
}, nil)
|
||||
return ctx
|
||||
}
|
||||
|
||||
// BenchmarkRateLimiterAllow 测试单客户端 Allow 性能。
|
||||
func BenchmarkRateLimiterAllow(b *testing.B) {
|
||||
cfg := &config.RateLimitConfig{
|
||||
RequestRate: 1000,
|
||||
Burst: 2000,
|
||||
Key: "ip",
|
||||
}
|
||||
rl, _ := NewRateLimiter(cfg)
|
||||
defer rl.(*RateLimiter).StopCleanup()
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
rl.(*RateLimiter).Allow("192.168.1.100")
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkRateLimiterAllowParallel_10Clients 测试 10 客户端并发 Allow 性能。
|
||||
func BenchmarkRateLimiterAllowParallel_10Clients(b *testing.B) {
|
||||
cfg := &config.RateLimitConfig{
|
||||
RequestRate: 10000,
|
||||
Burst: 20000,
|
||||
Key: "ip",
|
||||
}
|
||||
rl, _ := NewRateLimiter(cfg)
|
||||
defer rl.(*RateLimiter).StopCleanup()
|
||||
|
||||
clients := make([]string, 10)
|
||||
for i := range clients {
|
||||
clients[i] = "192.168.1." + string(rune('0'+i))
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
rl.(*RateLimiter).Allow(clients[i%10])
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// BenchmarkRateLimiterAllowParallel_100Clients 测试 100 客户端并发 Allow 性能。
|
||||
func BenchmarkRateLimiterAllowParallel_100Clients(b *testing.B) {
|
||||
cfg := &config.RateLimitConfig{
|
||||
RequestRate: 100000,
|
||||
Burst: 200000,
|
||||
Key: "ip",
|
||||
}
|
||||
rl, _ := NewRateLimiter(cfg)
|
||||
defer rl.(*RateLimiter).StopCleanup()
|
||||
|
||||
clients := make([]string, 100)
|
||||
for i := range clients {
|
||||
clients[i] = "10.0.0." + string(rune(i))
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
rl.(*RateLimiter).Allow(clients[i%100])
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// BenchmarkRateLimiterAllowParallel_1000Clients 测试 1000 客户端并发 Allow 性能。
|
||||
func BenchmarkRateLimiterAllowParallel_1000Clients(b *testing.B) {
|
||||
cfg := &config.RateLimitConfig{
|
||||
RequestRate: 1000000,
|
||||
Burst: 2000000,
|
||||
Key: "ip",
|
||||
}
|
||||
rl, _ := NewRateLimiter(cfg)
|
||||
defer rl.(*RateLimiter).StopCleanup()
|
||||
|
||||
clients := make([]string, 1000)
|
||||
for i := range clients {
|
||||
clients[i] = "172.16.0." + string(rune(i%256))
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
rl.(*RateLimiter).Allow(clients[i%1000])
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// BenchmarkRateLimiterCleanup_1000Buckets 测试清理 1000 个过期桶的性能。
|
||||
func BenchmarkRateLimiterCleanup_1000Buckets(b *testing.B) {
|
||||
cfg := &config.RateLimitConfig{
|
||||
RequestRate: 100,
|
||||
Burst: 200,
|
||||
Key: "ip",
|
||||
}
|
||||
mw, _ := NewRateLimiter(cfg)
|
||||
rl := mw.(*RateLimiter)
|
||||
defer rl.StopCleanup()
|
||||
|
||||
// 预创建 1000 个桶
|
||||
for i := 0; i < 1000; i++ {
|
||||
rl.Allow("192.168.0." + string(rune(i)))
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
rl.Cleanup(0) // 清理所有桶
|
||||
// 重新创建桶以保持测试一致性
|
||||
for j := 0; j < 1000; j++ {
|
||||
rl.Allow("192.168.0." + string(rune(j)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkKeyByIP 测试 IP 提取性能。
|
||||
func BenchmarkKeyByIP(b *testing.B) {
|
||||
ctx := setupRateLimitRequestCtx("192.168.1.100")
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
keyByIP(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkRateLimiterMiddleware 组件级测试:测量限流中间件本身的开销。
|
||||
func BenchmarkRateLimiterMiddleware(b *testing.B) {
|
||||
cfg := &config.RateLimitConfig{
|
||||
RequestRate: 100000,
|
||||
Burst: 200000,
|
||||
Key: "ip",
|
||||
}
|
||||
rl, _ := NewRateLimiter(cfg)
|
||||
defer rl.(*RateLimiter).StopCleanup()
|
||||
|
||||
mockHandler := func(ctx *fasthttp.RequestCtx) {
|
||||
ctx.SetStatusCode(fasthttp.StatusOK)
|
||||
ctx.SetBodyString("OK")
|
||||
}
|
||||
|
||||
handler := rl.Process(mockHandler)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
ctx := setupRateLimitRequestCtx("192.168.1.100")
|
||||
handler(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkRateLimiterMiddlewareParallel 测试并发场景下的中间件性能。
|
||||
func BenchmarkRateLimiterMiddlewareParallel(b *testing.B) {
|
||||
cfg := &config.RateLimitConfig{
|
||||
RequestRate: 1000000,
|
||||
Burst: 2000000,
|
||||
Key: "ip",
|
||||
}
|
||||
rl, _ := NewRateLimiter(cfg)
|
||||
defer rl.(*RateLimiter).StopCleanup()
|
||||
|
||||
mockHandler := func(ctx *fasthttp.RequestCtx) {
|
||||
ctx.SetStatusCode(fasthttp.StatusOK)
|
||||
ctx.SetBodyString("OK")
|
||||
}
|
||||
|
||||
handler := rl.Process(mockHandler)
|
||||
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
ip := "192.168.1." + string(rune('0'+i%10))
|
||||
ctx := setupRateLimitRequestCtx(ip)
|
||||
handler(ctx)
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// BenchmarkRateLimiterStats 测试获取统计信息的性能。
|
||||
func BenchmarkRateLimiterStats(b *testing.B) {
|
||||
cfg := &config.RateLimitConfig{
|
||||
RequestRate: 1000,
|
||||
Burst: 2000,
|
||||
Key: "ip",
|
||||
}
|
||||
mw, _ := NewRateLimiter(cfg)
|
||||
rl := mw.(*RateLimiter)
|
||||
defer rl.StopCleanup()
|
||||
|
||||
// 预创建一些桶
|
||||
for i := 0; i < 100; i++ {
|
||||
rl.Allow("192.168.0." + string(rune(i)))
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
rl.GetStats()
|
||||
}
|
||||
}
|
||||
142
internal/middleware/security/sliding_window_bench_test.go
Normal file
142
internal/middleware/security/sliding_window_bench_test.go
Normal file
@ -0,0 +1,142 @@
|
||||
// Package security 提供滑动窗口限流器的基准测试。
|
||||
//
|
||||
// 该文件测试近似模式和精确模式的滑动窗口限流性能。
|
||||
//
|
||||
// 作者:xfy
|
||||
package security
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// BenchmarkSlidingWindowAllow 测试近似模式滑动窗口 Allow 性能。
|
||||
func BenchmarkSlidingWindowAllow(b *testing.B) {
|
||||
sw := NewSlidingWindowLimiter(time.Second, 10000, false)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
sw.Allow("192.168.1.100")
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkSlidingWindowAllowPrecise 测试精确模式滑动窗口 Allow 性能。
|
||||
func BenchmarkSlidingWindowAllowPrecise(b *testing.B) {
|
||||
sw := NewSlidingWindowLimiter(time.Second, 10000, true)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
sw.Allow("192.168.1.100")
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkSlidingWindowAllowParallel 测试近似模式并发 Allow 性能。
|
||||
func BenchmarkSlidingWindowAllowParallel(b *testing.B) {
|
||||
sw := NewSlidingWindowLimiter(time.Second, 100000, false)
|
||||
|
||||
clients := make([]string, 10)
|
||||
for i := range clients {
|
||||
clients[i] = "192.168.1." + string(rune('0'+i))
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
sw.Allow(clients[i%10])
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// BenchmarkSlidingWindowAllowPreciseParallel 测试精确模式并发 Allow 性能。
|
||||
func BenchmarkSlidingWindowAllowPreciseParallel(b *testing.B) {
|
||||
sw := NewSlidingWindowLimiter(time.Second, 100000, true)
|
||||
|
||||
clients := make([]string, 10)
|
||||
for i := range clients {
|
||||
clients[i] = "192.168.1." + string(rune('0'+i))
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
sw.Allow(clients[i%10])
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// BenchmarkSlidingWindowCleanup 测试滑动窗口清理性能。
|
||||
func BenchmarkSlidingWindowCleanup(b *testing.B) {
|
||||
sw := NewSlidingWindowLimiter(time.Second, 1000, false)
|
||||
|
||||
// 预创建 100 个键
|
||||
for i := 0; i < 100; i++ {
|
||||
sw.Allow("192.168.0." + string(rune(i)))
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
sw.Cleanup(time.Hour)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkSlidingWindowGetCount 测试获取计数性能。
|
||||
func BenchmarkSlidingWindowGetCount(b *testing.B) {
|
||||
sw := NewSlidingWindowLimiter(time.Second, 10000, false)
|
||||
key := "192.168.1.100"
|
||||
|
||||
// 预先添加一些请求
|
||||
for i := 0; i < 100; i++ {
|
||||
sw.Allow(key)
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
sw.GetCount(key)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkSlidingWindowReset 测试重置性能。
|
||||
func BenchmarkSlidingWindowReset(b *testing.B) {
|
||||
sw := NewSlidingWindowLimiter(time.Second, 10000, false)
|
||||
key := "192.168.1.100"
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
sw.Allow(key)
|
||||
sw.Reset(key)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkSlidingWindowMultiKey 测试多键场景性能。
|
||||
func BenchmarkSlidingWindowMultiKey(b *testing.B) {
|
||||
sw := NewSlidingWindowLimiter(time.Second, 10000, false)
|
||||
|
||||
keys := make([]string, 100)
|
||||
for i := range keys {
|
||||
keys[i] = "192.168.0." + string(rune(i))
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
sw.Allow(keys[i%100])
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkSlidingWindowStats 测试获取统计信息性能。
|
||||
func BenchmarkSlidingWindowStats(b *testing.B) {
|
||||
sw := NewSlidingWindowLimiter(time.Second, 10000, false)
|
||||
|
||||
// 预创建一些键
|
||||
for i := 0; i < 50; i++ {
|
||||
sw.Allow("192.168.0." + string(rune(i)))
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
sw.GetStats()
|
||||
}
|
||||
}
|
||||
212
internal/variable/variable_bench_test.go
Normal file
212
internal/variable/variable_bench_test.go
Normal file
@ -0,0 +1,212 @@
|
||||
// Package variable 提供变量模块的基准测试。
|
||||
//
|
||||
// 该文件测试变量展开和 Pool 操作的性能。
|
||||
//
|
||||
// 作者:xfy
|
||||
package variable
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
// setupBenchmarkRequestCtx 创建用于基准测试的 fasthttp.RequestCtx。
|
||||
func setupBenchmarkRequestCtx() *fasthttp.RequestCtx {
|
||||
ctx := &fasthttp.RequestCtx{}
|
||||
ctx.Request.Header.SetMethod(fasthttp.MethodGet)
|
||||
ctx.Request.SetRequestURI("/test/path?foo=bar&baz=qux")
|
||||
ctx.Request.Header.SetHost("example.com")
|
||||
ctx.Init(&fasthttp.Request{}, &net.TCPAddr{
|
||||
IP: net.ParseIP("192.168.1.100"),
|
||||
Port: 12345,
|
||||
}, nil)
|
||||
return ctx
|
||||
}
|
||||
|
||||
// BenchmarkVariableExpandSimple 测试简单模板展开性能。
|
||||
//
|
||||
// 模板: "$remote_addr - $request_method"
|
||||
func BenchmarkVariableExpandSimple(b *testing.B) {
|
||||
ctx := setupBenchmarkRequestCtx()
|
||||
vc := NewVariableContext(ctx)
|
||||
defer ReleaseVariableContext(vc)
|
||||
|
||||
template := "$remote_addr - $request_method"
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
vc.Expand(template)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkVariableExpandComplex 测试复杂模板展开性能。
|
||||
//
|
||||
// 模拟 Nginx combined 日志格式:
|
||||
// "$remote_addr - [$time_local] \"$request_method $uri $args\" $status $body_bytes_sent"
|
||||
func BenchmarkVariableExpandComplex(b *testing.B) {
|
||||
ctx := setupBenchmarkRequestCtx()
|
||||
vc := NewVariableContext(ctx)
|
||||
vc.SetResponseInfo(200, 1024, 1000000) // status, bodySize, durationNs
|
||||
defer ReleaseVariableContext(vc)
|
||||
|
||||
template := "$remote_addr - [$time_local] \"$request_method $uri $args\" $status $body_bytes_sent"
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
vc.Expand(template)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkVariableExpandMixed 测试混合 ${var} 和 $var 格式的展开性能。
|
||||
func BenchmarkVariableExpandMixed(b *testing.B) {
|
||||
ctx := setupBenchmarkRequestCtx()
|
||||
vc := NewVariableContext(ctx)
|
||||
defer ReleaseVariableContext(vc)
|
||||
|
||||
template := "${remote_addr} - $request_method ${uri}?${args}"
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
vc.Expand(template)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkVariableExpandNoVar 测试无变量模板的性能(快速路径)。
|
||||
func BenchmarkVariableExpandNoVar(b *testing.B) {
|
||||
ctx := setupBenchmarkRequestCtx()
|
||||
vc := NewVariableContext(ctx)
|
||||
defer ReleaseVariableContext(vc)
|
||||
|
||||
template := "This is a plain string with no variables"
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
vc.Expand(template)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkVariableContextPool 测试 Pool 获取释放性能。
|
||||
func BenchmarkVariableContextPool(b *testing.B) {
|
||||
ctx := setupBenchmarkRequestCtx()
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
vc := NewVariableContext(ctx)
|
||||
ReleaseVariableContext(vc)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkVariableContextPoolParallel 测试并发 Pool 获取释放性能。
|
||||
func BenchmarkVariableContextPoolParallel(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
ctx := setupBenchmarkRequestCtx()
|
||||
for pb.Next() {
|
||||
vc := NewVariableContext(ctx)
|
||||
ReleaseVariableContext(vc)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// BenchmarkVariableGetCache 测试内置变量缓存命中性能。
|
||||
//
|
||||
// 首次获取变量会求值并缓存,后续获取命中缓存。
|
||||
func BenchmarkVariableGetCache(b *testing.B) {
|
||||
ctx := setupBenchmarkRequestCtx()
|
||||
vc := NewVariableContext(ctx)
|
||||
defer ReleaseVariableContext(vc)
|
||||
|
||||
// 预热缓存
|
||||
_, _ = vc.Get("remote_addr")
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
vc.Get("remote_addr")
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkVariableGetNoCache 测试内置变量首次求值性能。
|
||||
//
|
||||
// 每次循环创建新的 VariableContext,模拟首次求值场景。
|
||||
func BenchmarkVariableGetNoCache(b *testing.B) {
|
||||
ctx := setupBenchmarkRequestCtx()
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
vc := NewVariableContext(ctx)
|
||||
vc.Get("remote_addr")
|
||||
ReleaseVariableContext(vc)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkVariableGetMultiple 测试获取多个内置变量的性能。
|
||||
func BenchmarkVariableGetMultiple(b *testing.B) {
|
||||
ctx := setupBenchmarkRequestCtx()
|
||||
vc := NewVariableContext(ctx)
|
||||
defer ReleaseVariableContext(vc)
|
||||
|
||||
vars := []string{
|
||||
"remote_addr", "request_method", "uri", "args",
|
||||
"host", "request_uri", "scheme", "time_local",
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, name := range vars {
|
||||
vc.Get(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkVariableSetAndGet 测试设置和获取自定义变量的性能。
|
||||
func BenchmarkVariableSetAndGet(b *testing.B) {
|
||||
ctx := setupBenchmarkRequestCtx()
|
||||
vc := NewVariableContext(ctx)
|
||||
defer ReleaseVariableContext(vc)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
vc.Set("custom_var", "custom_value")
|
||||
vc.Get("custom_var")
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkExpandStringStaticWithLookup 测试静态展开函数的性能(使用自定义查找函数)。
|
||||
func BenchmarkExpandStringStaticWithLookup(b *testing.B) {
|
||||
template := "$remote_addr - $request_method"
|
||||
lookup := func(name string) string {
|
||||
switch name {
|
||||
case "remote_addr":
|
||||
return "192.168.1.100"
|
||||
case "request_method":
|
||||
return "GET"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
ExpandString(template, lookup)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkVariableExpandLongTemplate 测试长模板展开性能。
|
||||
//
|
||||
// 模拟完整访问日志格式,约 200 字符。
|
||||
func BenchmarkVariableExpandLongTemplate(b *testing.B) {
|
||||
ctx := setupBenchmarkRequestCtx()
|
||||
vc := NewVariableContext(ctx)
|
||||
vc.SetResponseInfo(200, 4096, 15000000)
|
||||
vc.SetServerName("api.example.com")
|
||||
defer ReleaseVariableContext(vc)
|
||||
|
||||
template := "$remote_addr - [$time_local] \"$request_method $uri?$args\" $status $body_bytes_sent \"$http_referer\" \"$http_user_agent\" $request_time $server_name"
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
vc.Expand(template)
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user