test(middleware/bodylimit,rewrite): 添加性能基准测试
为 middleware 子模块添加 benchmark 测试: - bodylimit: 请求体大小限制检查 - rewrite: URL 重写规则匹配 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
1b40a72632
commit
4bf7318816
147
internal/middleware/bodylimit/bodylimit_bench_test.go
Normal file
147
internal/middleware/bodylimit/bodylimit_bench_test.go
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
// Package bodylimit 提供请求体大小限制中间件的基准测试。
|
||||||
|
//
|
||||||
|
// 作者:xfy
|
||||||
|
package bodylimit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/valyala/fasthttp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BenchmarkBodyLimitProcess 基准测试限制检查(无超限情况)。
|
||||||
|
//
|
||||||
|
// 测试中间件处理正常请求(不触发限制)的性能。
|
||||||
|
func BenchmarkBodyLimitProcess(b *testing.B) {
|
||||||
|
bl, err := New("1mb")
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("创建中间件失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nextHandler := func(ctx *fasthttp.RequestCtx) {
|
||||||
|
ctx.SetStatusCode(fasthttp.StatusOK)
|
||||||
|
}
|
||||||
|
handler := bl.Process(nextHandler)
|
||||||
|
|
||||||
|
// 准备请求上下文
|
||||||
|
body := []byte("test body content")
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
b.ReportAllocs()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
ctx := &fasthttp.RequestCtx{}
|
||||||
|
ctx.Request.Header.SetMethod("POST")
|
||||||
|
ctx.Request.Header.SetContentLength(len(body))
|
||||||
|
ctx.Request.SetBodyStream(bytes.NewReader(body), len(body))
|
||||||
|
|
||||||
|
handler(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkBodyLimitGetLimit 基准测试获取路径限制。
|
||||||
|
//
|
||||||
|
// 测试 GetLimit 方法的性能,该方法在每次请求时都会被调用。
|
||||||
|
func BenchmarkBodyLimitGetLimit(b *testing.B) {
|
||||||
|
bl, err := New("1mb")
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("创建中间件失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加多个路径配置
|
||||||
|
paths := []string{
|
||||||
|
"/api/v1/users",
|
||||||
|
"/api/v1/posts",
|
||||||
|
"/api/v2/upload",
|
||||||
|
"/admin/settings",
|
||||||
|
"/static/images",
|
||||||
|
}
|
||||||
|
limits := []string{"10kb", "100kb", "5mb", "50kb", "20mb"}
|
||||||
|
|
||||||
|
for i, path := range paths {
|
||||||
|
if err := bl.AddPathLimit(path, limits[i]); err != nil {
|
||||||
|
b.Fatalf("添加路径限制失败: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testPaths := []string{
|
||||||
|
"/api/v1/users/123",
|
||||||
|
"/api/v2/upload/file",
|
||||||
|
"/other/path",
|
||||||
|
"/admin/settings/general",
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
b.ReportAllocs()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
path := testPaths[i%len(testPaths)]
|
||||||
|
_ = bl.GetLimit(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkBodyLimitPathMatching 基准测试多路径配置匹配。
|
||||||
|
//
|
||||||
|
// 测试在有大量路径配置时 GetLimit 的性能(包括 RWMutex 锁开销)。
|
||||||
|
func BenchmarkBodyLimitPathMatching(b *testing.B) {
|
||||||
|
bl, err := New("1mb")
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("创建中间件失败: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加大量路径配置
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
path := "/api/v" + string(rune('0'+i%10)) + "/resource" + string(rune('0'+i%10))
|
||||||
|
size := "1mb"
|
||||||
|
if i%3 == 0 {
|
||||||
|
size = "10mb"
|
||||||
|
} else if i%5 == 0 {
|
||||||
|
size = "100kb"
|
||||||
|
}
|
||||||
|
if err := bl.AddPathLimit(path, size); err != nil {
|
||||||
|
b.Fatalf("添加路径限制失败: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
testPaths := []string{
|
||||||
|
"/api/v1/resource1/123",
|
||||||
|
"/api/v5/resource5/upload",
|
||||||
|
"/other/path",
|
||||||
|
"/api/v9/resource9/data",
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
b.ReportAllocs()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
path := testPaths[i%len(testPaths)]
|
||||||
|
_ = bl.GetLimit(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkParseSize 基准测试大小字符串解析。
|
||||||
|
//
|
||||||
|
// 测试 ParseSize 函数解析各种大小字符串的性能。
|
||||||
|
func BenchmarkParseSize(b *testing.B) {
|
||||||
|
sizes := []string{
|
||||||
|
"1024",
|
||||||
|
"1kb",
|
||||||
|
"10kb",
|
||||||
|
"1mb",
|
||||||
|
"10mb",
|
||||||
|
"1.5mb",
|
||||||
|
"1gb",
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
b.ReportAllocs()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
sizeStr := sizes[i%len(sizes)]
|
||||||
|
_, err := ParseSize(sizeStr)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("ParseSize(%q) 失败: %v", sizeStr, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
254
internal/middleware/rewrite/rewrite_bench_test.go
Normal file
254
internal/middleware/rewrite/rewrite_bench_test.go
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
// Package rewrite URL 重写中间件基准测试
|
||||||
|
package rewrite
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/valyala/fasthttp"
|
||||||
|
"rua.plus/lolly/internal/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BenchmarkRewriteProcess 基准测试:正则匹配 + 替换
|
||||||
|
// 测试单个重写规则的性能
|
||||||
|
func BenchmarkRewriteProcess(b *testing.B) {
|
||||||
|
m, err := New([]config.RewriteRule{
|
||||||
|
{Pattern: "^/api/v1/(.*)$", Replacement: "/api/v2/$1", Flag: "last"},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("New() error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nextHandler := func(ctx *fasthttp.RequestCtx) {
|
||||||
|
ctx.WriteString("OK")
|
||||||
|
}
|
||||||
|
handler := m.Process(nextHandler)
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
b.ReportAllocs()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
ctx := &fasthttp.RequestCtx{}
|
||||||
|
ctx.Request.SetRequestURI("/api/v1/users")
|
||||||
|
handler(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkRewriteMultipleRules 基准测试:多规则匹配(最多 10 个)
|
||||||
|
// 测试规则列表的遍历和匹配性能
|
||||||
|
func BenchmarkRewriteMultipleRules(b *testing.B) {
|
||||||
|
// 创建 10 个规则,只有最后一个匹配
|
||||||
|
rules := []config.RewriteRule{
|
||||||
|
{Pattern: "^/a/(.*)$", Replacement: "/x/$1", Flag: "last"},
|
||||||
|
{Pattern: "^/b/(.*)$", Replacement: "/x/$1", Flag: "last"},
|
||||||
|
{Pattern: "^/c/(.*)$", Replacement: "/x/$1", Flag: "last"},
|
||||||
|
{Pattern: "^/d/(.*)$", Replacement: "/x/$1", Flag: "last"},
|
||||||
|
{Pattern: "^/e/(.*)$", Replacement: "/x/$1", Flag: "last"},
|
||||||
|
{Pattern: "^/f/(.*)$", Replacement: "/x/$1", Flag: "last"},
|
||||||
|
{Pattern: "^/g/(.*)$", Replacement: "/x/$1", Flag: "last"},
|
||||||
|
{Pattern: "^/h/(.*)$", Replacement: "/x/$1", Flag: "last"},
|
||||||
|
{Pattern: "^/i/(.*)$", Replacement: "/x/$1", Flag: "last"},
|
||||||
|
{Pattern: "^/j/(.*)$", Replacement: "/x/$1", Flag: "last"},
|
||||||
|
}
|
||||||
|
|
||||||
|
m, err := New(rules)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("New() error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nextHandler := func(ctx *fasthttp.RequestCtx) {
|
||||||
|
ctx.WriteString("OK")
|
||||||
|
}
|
||||||
|
handler := m.Process(nextHandler)
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
b.ReportAllocs()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
ctx := &fasthttp.RequestCtx{}
|
||||||
|
ctx.Request.SetRequestURI("/j/resource")
|
||||||
|
handler(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkRewriteWithVariableExpand 基准测试:带变量展开
|
||||||
|
// 测试变量展开对性能的影响
|
||||||
|
func BenchmarkRewriteWithVariableExpand(b *testing.B) {
|
||||||
|
// 在替换字符串中使用变量
|
||||||
|
m, err := New([]config.RewriteRule{
|
||||||
|
{Pattern: "^/api/(.*)$", Replacement: "/proxy/${host}/$1", Flag: "last"},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("New() error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nextHandler := func(ctx *fasthttp.RequestCtx) {
|
||||||
|
ctx.WriteString("OK")
|
||||||
|
}
|
||||||
|
handler := m.Process(nextHandler)
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
b.ReportAllocs()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
ctx := &fasthttp.RequestCtx{}
|
||||||
|
ctx.Request.SetRequestURI("/api/data")
|
||||||
|
// 设置 Host 头以供变量展开使用
|
||||||
|
ctx.Request.Header.Set("Host", "example.com")
|
||||||
|
handler(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkRewriteFlagLast 基准测试:FlagLast 循环检测
|
||||||
|
// 测试 FlagLast 重新扫描的性能
|
||||||
|
func BenchmarkRewriteFlagLast(b *testing.B) {
|
||||||
|
// 创建两条规则形成链式重写
|
||||||
|
// /v1/* -> /v2/* -> /v3/*
|
||||||
|
m, err := New([]config.RewriteRule{
|
||||||
|
{Pattern: "^/v1/(.*)$", Replacement: "/v2/$1", Flag: "last"},
|
||||||
|
{Pattern: "^/v2/(.*)$", Replacement: "/v3/$1", Flag: "last"},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("New() error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nextHandler := func(ctx *fasthttp.RequestCtx) {
|
||||||
|
ctx.WriteString("OK")
|
||||||
|
}
|
||||||
|
handler := m.Process(nextHandler)
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
b.ReportAllocs()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
ctx := &fasthttp.RequestCtx{}
|
||||||
|
ctx.Request.SetRequestURI("/v1/resource")
|
||||||
|
handler(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkRewriteNoMatch 基准测试:无匹配情况
|
||||||
|
// 测试当没有规则匹配时的性能
|
||||||
|
func BenchmarkRewriteNoMatch(b *testing.B) {
|
||||||
|
m, err := New([]config.RewriteRule{
|
||||||
|
{Pattern: "^/api/(.*)$", Replacement: "/proxy/$1", Flag: "last"},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("New() error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nextHandler := func(ctx *fasthttp.RequestCtx) {
|
||||||
|
ctx.WriteString("OK")
|
||||||
|
}
|
||||||
|
handler := m.Process(nextHandler)
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
b.ReportAllocs()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
ctx := &fasthttp.RequestCtx{}
|
||||||
|
ctx.Request.SetRequestURI("/static/file.txt")
|
||||||
|
handler(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkRewriteComplexPattern 基准测试:复杂正则表达式
|
||||||
|
// 测试复杂正则匹配的性能
|
||||||
|
func BenchmarkRewriteComplexPattern(b *testing.B) {
|
||||||
|
// 使用更复杂的正则表达式
|
||||||
|
m, err := New([]config.RewriteRule{
|
||||||
|
{Pattern: "^/api/v\\d+/(users|posts|comments)/(\\d+)/(profile|settings)$", Replacement: "/internal/$1/$2/$3", Flag: "last"},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("New() error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nextHandler := func(ctx *fasthttp.RequestCtx) {
|
||||||
|
ctx.WriteString("OK")
|
||||||
|
}
|
||||||
|
handler := m.Process(nextHandler)
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
b.ReportAllocs()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
ctx := &fasthttp.RequestCtx{}
|
||||||
|
ctx.Request.SetRequestURI("/api/v1/users/123/profile")
|
||||||
|
handler(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkRewriteRedirect 基准测试:重定向标志
|
||||||
|
// 测试重定向响应的性能
|
||||||
|
func BenchmarkRewriteRedirect(b *testing.B) {
|
||||||
|
m, err := New([]config.RewriteRule{
|
||||||
|
{Pattern: "^/old/(.*)$", Replacement: "/new/$1", Flag: "redirect"},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("New() error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nextHandler := func(ctx *fasthttp.RequestCtx) {
|
||||||
|
ctx.WriteString("OK")
|
||||||
|
}
|
||||||
|
handler := m.Process(nextHandler)
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
b.ReportAllocs()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
ctx := &fasthttp.RequestCtx{}
|
||||||
|
ctx.Request.SetRequestURI("/old/page")
|
||||||
|
handler(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkRewriteBreak 基准测试:Break 标志
|
||||||
|
// 测试 Break 标志提前终止的性能
|
||||||
|
func BenchmarkRewriteBreak(b *testing.B) {
|
||||||
|
m, err := New([]config.RewriteRule{
|
||||||
|
{Pattern: "^/api/(.*)$", Replacement: "/internal/$1", Flag: "break"},
|
||||||
|
{Pattern: "^/internal/(.*)$", Replacement: "/final/$1", Flag: "last"},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("New() error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nextHandler := func(ctx *fasthttp.RequestCtx) {
|
||||||
|
ctx.WriteString("OK")
|
||||||
|
}
|
||||||
|
handler := m.Process(nextHandler)
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
b.ReportAllocs()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
ctx := &fasthttp.RequestCtx{}
|
||||||
|
ctx.Request.SetRequestURI("/api/data")
|
||||||
|
handler(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BenchmarkRewriteMultipleCaptures 基准测试:多捕获组
|
||||||
|
// 测试多个捕获组的替换性能
|
||||||
|
func BenchmarkRewriteMultipleCaptures(b *testing.B) {
|
||||||
|
m, err := New([]config.RewriteRule{
|
||||||
|
{Pattern: "^/(\\d{4})/(\\d{2})/(\\d{2})/(.*)$", Replacement: "/archive/$1-$2-$3/$4", Flag: "last"},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
b.Fatalf("New() error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nextHandler := func(ctx *fasthttp.RequestCtx) {
|
||||||
|
ctx.WriteString("OK")
|
||||||
|
}
|
||||||
|
handler := m.Process(nextHandler)
|
||||||
|
|
||||||
|
b.ResetTimer()
|
||||||
|
b.ReportAllocs()
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
ctx := &fasthttp.RequestCtx{}
|
||||||
|
ctx.Request.SetRequestURI("/2024/03/15/article")
|
||||||
|
handler(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user