- 所有 *_bench_test.go 文件从 for i := 0; i < b.N; i++ 改为 for b.Loop() - 部分测试文件从 for i := 0; i < N; ... 改为 for range N 或 for i := range N - 涵盖模块: cache, handler, http2, http3, loadbalance, logging, lua, middleware/accesslog, middleware/bodylimit, middleware/rewrite, middleware/security, netutil, resolver, server, ssl, stream Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
310 lines
7.8 KiB
Go
310 lines
7.8 KiB
Go
// Package http3 提供 HTTP/3 适配器的基准测试。
|
||
//
|
||
// 该文件测试 HTTP/3 适配器的性能,包括:
|
||
// - Handler 包装开销
|
||
// - HTTP 请求到 fasthttp 请求的转换性能
|
||
// - 不同大小 Body 的读取性能
|
||
// - fasthttp 响应到 HTTP 响应的转换性能
|
||
// - RequestCtx sync.Pool 复用效率
|
||
//
|
||
// 作者:xfy
|
||
package http3
|
||
|
||
import (
|
||
"bytes"
|
||
"io"
|
||
"net/http"
|
||
"net/url"
|
||
"testing"
|
||
|
||
"github.com/valyala/fasthttp"
|
||
)
|
||
|
||
// BenchmarkAdapterWrap 测试 Handler 包装开销
|
||
//
|
||
// 该基准测试测量将 fasthttp.RequestHandler 包装为 http.Handler 的基本开销,
|
||
// 包括 ctxPool 获取和放回操作。
|
||
func BenchmarkAdapterWrap(b *testing.B) {
|
||
adapter := NewAdapter()
|
||
|
||
// 简单的 fasthttp handler
|
||
handler := func(ctx *fasthttp.RequestCtx) {
|
||
ctx.SetStatusCode(200)
|
||
ctx.SetBodyString("OK")
|
||
}
|
||
|
||
httpHandler := adapter.Wrap(handler)
|
||
|
||
// 创建请求
|
||
req := &http.Request{
|
||
Method: "GET",
|
||
URL: &url.URL{Path: "/test"},
|
||
Host: "localhost",
|
||
Header: http.Header{},
|
||
}
|
||
|
||
b.ResetTimer()
|
||
b.ReportAllocs()
|
||
|
||
for b.Loop() {
|
||
rw := &mockResponseWriter{}
|
||
httpHandler.ServeHTTP(rw, req)
|
||
}
|
||
}
|
||
|
||
// BenchmarkAdapterConvertRequest 测试 HTTP -> fasthttp 头部转换性能
|
||
//
|
||
// 该基准测试测量 convertRequest 方法在仅有头部转换时的性能,
|
||
// 不包含 Body 读取的开销。
|
||
func BenchmarkAdapterConvertRequest(b *testing.B) {
|
||
adapter := NewAdapter()
|
||
|
||
// 创建包含多个头部的请求
|
||
req := &http.Request{
|
||
Method: "POST",
|
||
URL: &url.URL{Path: "/api/v1/users", RawQuery: "page=1&limit=10"},
|
||
Host: "api.example.com",
|
||
Header: http.Header{
|
||
"Content-Type": []string{"application/json"},
|
||
"Accept": []string{"application/json"},
|
||
"Authorization": []string{"Bearer token123456"},
|
||
"X-Request-ID": []string{"req-123456789"},
|
||
"X-User-Agent": []string{"TestClient/1.0"},
|
||
"Accept-Encoding": []string{"gzip, deflate"},
|
||
"Cache-Control": []string{"no-cache"},
|
||
"Connection": []string{"keep-alive"},
|
||
},
|
||
RemoteAddr: "192.168.1.100:12345",
|
||
}
|
||
|
||
ctx := &fasthttp.RequestCtx{}
|
||
ctx.Init(&fasthttp.Request{}, nil, nil)
|
||
|
||
b.ResetTimer()
|
||
b.ReportAllocs()
|
||
|
||
for b.Loop() {
|
||
ctx.Request.Reset()
|
||
adapter.convertRequest(req, ctx)
|
||
}
|
||
}
|
||
|
||
// BenchmarkAdapterConvertRequestBody_1KB 测试 1KB Body 读取性能
|
||
//
|
||
// 该基准测试测量 io.ReadAll 在 1KB Body 大小下的开销。
|
||
func BenchmarkAdapterConvertRequestBody_1KB(b *testing.B) {
|
||
benchmarkAdapterConvertRequestBody(b, 1024)
|
||
}
|
||
|
||
// BenchmarkAdapterConvertRequestBody_10KB 测试 10KB Body 读取性能
|
||
//
|
||
// 该基准测试测量 io.ReadAll 在 10KB Body 大小下的开销。
|
||
func BenchmarkAdapterConvertRequestBody_10KB(b *testing.B) {
|
||
benchmarkAdapterConvertRequestBody(b, 10*1024)
|
||
}
|
||
|
||
// BenchmarkAdapterConvertRequestBody_100KB 测试 100KB Body 读取性能
|
||
//
|
||
// 该基准测试测量 io.ReadAll 在 100KB Body 大小下的开销。
|
||
func BenchmarkAdapterConvertRequestBody_100KB(b *testing.B) {
|
||
benchmarkAdapterConvertRequestBody(b, 100*1024)
|
||
}
|
||
|
||
// benchmarkAdapterConvertRequestBody 是 Body 读取性能的通用基准测试函数
|
||
//
|
||
// 参数:
|
||
// - b: 测试对象
|
||
// - bodySize: Body 大小(字节)
|
||
func benchmarkAdapterConvertRequestBody(b *testing.B, bodySize int) {
|
||
adapter := NewAdapter()
|
||
bodyData := make([]byte, bodySize)
|
||
for i := range bodyData {
|
||
bodyData[i] = byte('a' + (i % 26))
|
||
}
|
||
|
||
b.ResetTimer()
|
||
b.ReportAllocs()
|
||
|
||
for b.Loop() {
|
||
b.StopTimer()
|
||
// 每次迭代创建新的 Body,模拟新的请求
|
||
req := &http.Request{
|
||
Method: "POST",
|
||
URL: &url.URL{Path: "/upload"},
|
||
Host: "localhost",
|
||
Header: http.Header{
|
||
"Content-Type": []string{"application/octet-stream"},
|
||
},
|
||
Body: io.NopCloser(bytes.NewReader(bodyData)),
|
||
ContentLength: int64(bodySize),
|
||
}
|
||
ctx := &fasthttp.RequestCtx{}
|
||
ctx.Init(&fasthttp.Request{}, nil, nil)
|
||
b.StartTimer()
|
||
|
||
adapter.convertRequest(req, ctx)
|
||
}
|
||
}
|
||
|
||
// BenchmarkAdapterConvertResponse 测试 fasthttp -> HTTP 响应转换性能
|
||
//
|
||
// 该基准测试测量 convertResponse 方法的性能,包括:
|
||
// - 状态码提取
|
||
// - 响应头部复制
|
||
// - 响应体写入
|
||
func BenchmarkAdapterConvertResponse(b *testing.B) {
|
||
adapter := NewAdapter()
|
||
|
||
// 预创建响应内容
|
||
responseBody := []byte(`{"status":"success","data":{"id":12345,"name":"test","items":[1,2,3,4,5]}}`)
|
||
|
||
ctx := &fasthttp.RequestCtx{}
|
||
ctx.Init(&fasthttp.Request{}, nil, nil)
|
||
ctx.SetStatusCode(200)
|
||
ctx.Response.Header.Set("Content-Type", "application/json")
|
||
ctx.Response.Header.Set("X-Response-ID", "resp-123456")
|
||
ctx.Response.Header.Set("Cache-Control", "no-store")
|
||
ctx.SetBody(responseBody)
|
||
|
||
b.ResetTimer()
|
||
b.ReportAllocs()
|
||
|
||
for b.Loop() {
|
||
b.StopTimer()
|
||
rw := &mockResponseWriter{}
|
||
b.StartTimer()
|
||
|
||
adapter.convertResponse(ctx, rw)
|
||
}
|
||
}
|
||
|
||
// BenchmarkAdapterCtxPool 测试 RequestCtx sync.Pool 复用效率
|
||
//
|
||
// 该基准测试比较使用 sync.Pool 复用 RequestCtx 与每次创建新对象的性能差异。
|
||
func BenchmarkAdapterCtxPool(b *testing.B) {
|
||
b.Run("WithPool", func(b *testing.B) {
|
||
adapter := NewAdapter()
|
||
handler := func(ctx *fasthttp.RequestCtx) {
|
||
ctx.SetStatusCode(200)
|
||
}
|
||
httpHandler := adapter.Wrap(handler)
|
||
req := &http.Request{
|
||
Method: "GET",
|
||
URL: &url.URL{Path: "/"},
|
||
Host: "localhost",
|
||
}
|
||
|
||
b.ResetTimer()
|
||
b.ReportAllocs()
|
||
|
||
for b.Loop() {
|
||
rw := &mockResponseWriter{}
|
||
httpHandler.ServeHTTP(rw, req)
|
||
}
|
||
})
|
||
|
||
b.Run("WithoutPool", func(b *testing.B) {
|
||
handler := func(ctx *fasthttp.RequestCtx) {
|
||
ctx.SetStatusCode(200)
|
||
}
|
||
req := &http.Request{
|
||
Method: "GET",
|
||
URL: &url.URL{Path: "/"},
|
||
Host: "localhost",
|
||
}
|
||
|
||
b.ResetTimer()
|
||
b.ReportAllocs()
|
||
|
||
for b.Loop() {
|
||
rw := &mockResponseWriter{}
|
||
// 每次创建新的 ctx,不使用 Pool
|
||
ctx := &fasthttp.RequestCtx{}
|
||
ctx.Init(&fasthttp.Request{}, nil, nil)
|
||
ctx.Request.Header.SetMethod(req.Method)
|
||
ctx.Request.SetRequestURI(req.URL.Path)
|
||
ctx.Request.Header.SetHost(req.Host)
|
||
handler(ctx)
|
||
// 模拟响应写入
|
||
if ctx.Response.StatusCode() == 0 {
|
||
rw.status = 200
|
||
} else {
|
||
rw.status = ctx.Response.StatusCode()
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// BenchmarkAdapterConvertRequest_Parallel 测试并发环境下的请求转换性能
|
||
//
|
||
// 该基准测试使用 b.RunParallel 模拟并发请求场景。
|
||
func BenchmarkAdapterConvertRequest_Parallel(b *testing.B) {
|
||
adapter := NewAdapter()
|
||
|
||
req := &http.Request{
|
||
Method: "POST",
|
||
URL: &url.URL{Path: "/api/data", RawQuery: "key=value"},
|
||
Host: "localhost",
|
||
Header: http.Header{
|
||
"Content-Type": []string{"application/json"},
|
||
"X-Request-ID": []string{"parallel-test"},
|
||
},
|
||
}
|
||
|
||
b.ResetTimer()
|
||
b.ReportAllocs()
|
||
|
||
b.RunParallel(func(pb *testing.PB) {
|
||
for pb.Next() {
|
||
ctx := &fasthttp.RequestCtx{}
|
||
ctx.Init(&fasthttp.Request{}, nil, nil)
|
||
adapter.convertRequest(req, ctx)
|
||
}
|
||
})
|
||
}
|
||
|
||
// BenchmarkAdapterFullRoundTrip 测试完整的请求-响应往返性能
|
||
//
|
||
// 该基准测试模拟真实的 HTTP/3 请求处理流程,包括:
|
||
// - 请求转换
|
||
// - fasthttp handler 执行
|
||
// - 响应转换
|
||
func BenchmarkAdapterFullRoundTrip(b *testing.B) {
|
||
adapter := NewAdapter()
|
||
|
||
// 模拟真实的 API handler
|
||
apiHandler := func(ctx *fasthttp.RequestCtx) {
|
||
// 模拟一些业务逻辑
|
||
method := string(ctx.Method())
|
||
path := string(ctx.Path())
|
||
|
||
if method == "GET" && string(path) == "/api/users" {
|
||
ctx.SetStatusCode(200)
|
||
ctx.Response.Header.Set("Content-Type", "application/json")
|
||
ctx.SetBodyString(`{"users":[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}]}`)
|
||
} else {
|
||
ctx.SetStatusCode(404)
|
||
ctx.SetBodyString(`{"error":"not found"}`)
|
||
}
|
||
}
|
||
|
||
httpHandler := adapter.Wrap(apiHandler)
|
||
|
||
req := &http.Request{
|
||
Method: "GET",
|
||
URL: &url.URL{Path: "/api/users"},
|
||
Host: "api.example.com",
|
||
Header: http.Header{
|
||
"Accept": []string{"application/json"},
|
||
},
|
||
}
|
||
|
||
b.ResetTimer()
|
||
b.ReportAllocs()
|
||
|
||
for b.Loop() {
|
||
rw := &mockResponseWriter{}
|
||
httpHandler.ServeHTTP(rw, req)
|
||
}
|
||
}
|