feat(variable): 添加 EphemeralGet 和 PersistentGet 零拷贝变量访问 API
- 新增 EphemeralGet() 方法返回 []byte,用于请求范围内的零拷贝访问 - 新增 PersistentGet() 方法返回 string,用于跨请求持久存储 - BuiltinVariable 结构体添加 GetterBytes 字段支持字节获取器 - Context 结构体添加 bytesCache map 缓存字节值 - 为 $host、$request_uri、$uri、$args、$request_method 添加字节获取器 - 添加完整的单元测试和性能基准测试 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
ddc9e0cef1
commit
dc7358bf4e
@ -49,6 +49,10 @@ func init() {
|
||||
Getter: func(ctx *fasthttp.RequestCtx) string {
|
||||
return string(ctx.Host())
|
||||
},
|
||||
GetterBytes: func(ctx *fasthttp.RequestCtx) []byte {
|
||||
// SAFETY: ctx.Host() returns []byte valid within request scope
|
||||
return ctx.Host()
|
||||
},
|
||||
})
|
||||
|
||||
// 2. $remote_addr - 客户端 IP
|
||||
@ -91,6 +95,10 @@ func init() {
|
||||
Getter: func(ctx *fasthttp.RequestCtx) string {
|
||||
return string(ctx.RequestURI())
|
||||
},
|
||||
GetterBytes: func(ctx *fasthttp.RequestCtx) []byte {
|
||||
// SAFETY: ctx.RequestURI() returns []byte valid within request scope
|
||||
return ctx.RequestURI()
|
||||
},
|
||||
})
|
||||
|
||||
// 5. $uri - 解码后的 URI 路径
|
||||
@ -100,6 +108,10 @@ func init() {
|
||||
Getter: func(ctx *fasthttp.RequestCtx) string {
|
||||
return string(ctx.Path())
|
||||
},
|
||||
GetterBytes: func(ctx *fasthttp.RequestCtx) []byte {
|
||||
// SAFETY: ctx.Path() returns []byte valid within request scope
|
||||
return ctx.Path()
|
||||
},
|
||||
})
|
||||
|
||||
// 6. $args - 查询参数字符串
|
||||
@ -109,6 +121,10 @@ func init() {
|
||||
Getter: func(ctx *fasthttp.RequestCtx) string {
|
||||
return string(ctx.QueryArgs().QueryString())
|
||||
},
|
||||
GetterBytes: func(ctx *fasthttp.RequestCtx) []byte {
|
||||
// SAFETY: ctx.QueryArgs().QueryString() returns []byte valid within request scope
|
||||
return ctx.QueryArgs().QueryString()
|
||||
},
|
||||
})
|
||||
|
||||
// 7. $request_method - 请求方法
|
||||
@ -118,6 +134,10 @@ func init() {
|
||||
Getter: func(ctx *fasthttp.RequestCtx) string {
|
||||
return string(ctx.Method())
|
||||
},
|
||||
GetterBytes: func(ctx *fasthttp.RequestCtx) []byte {
|
||||
// SAFETY: ctx.Method() returns []byte valid within request scope
|
||||
return ctx.Method()
|
||||
},
|
||||
})
|
||||
|
||||
// 8. $scheme - 协议
|
||||
|
||||
@ -29,6 +29,7 @@ import (
|
||||
// BuiltinVariable 内置变量定义
|
||||
type BuiltinVariable struct {
|
||||
Getter func(ctx *fasthttp.RequestCtx) string
|
||||
GetterBytes func(ctx *fasthttp.RequestCtx) []byte // 零拷贝 getter,用于 EphemeralGet
|
||||
Name string
|
||||
Description string
|
||||
}
|
||||
@ -37,7 +38,8 @@ type BuiltinVariable struct {
|
||||
type Context struct {
|
||||
ctx *fasthttp.RequestCtx
|
||||
store map[string]string
|
||||
cache map[string]string
|
||||
cache map[string]string // string 缓存(用于 PersistentGet)
|
||||
bytesCache map[string][]byte // []byte 缓存(用于 EphemeralGet)
|
||||
serverName string
|
||||
upstreamAddr string
|
||||
status int
|
||||
@ -53,8 +55,9 @@ type Context struct {
|
||||
var pool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &Context{
|
||||
store: make(map[string]string),
|
||||
cache: make(map[string]string),
|
||||
store: make(map[string]string),
|
||||
cache: make(map[string]string),
|
||||
bytesCache: make(map[string][]byte),
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -133,6 +136,10 @@ func NewContext(ctx *fasthttp.RequestCtx) *Context {
|
||||
for k := range vc.cache {
|
||||
delete(vc.cache, k)
|
||||
}
|
||||
// 清空内置变量 bytes 缓存
|
||||
for k := range vc.bytesCache {
|
||||
delete(vc.bytesCache, k)
|
||||
}
|
||||
// 清空自定义变量 store
|
||||
for k := range vc.store {
|
||||
delete(vc.store, k)
|
||||
@ -272,6 +279,155 @@ func (vc *Context) Get(name string) (string, bool) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
// EphemeralGet returns []byte view valid only within request scope.
|
||||
//
|
||||
// WARNING: The returned []byte becomes INVALID after request completes.
|
||||
// SAFETY: Use only for immediate consumption (logging, header write).
|
||||
// For persistent storage, use PersistentGet().
|
||||
//
|
||||
// This method provides zero-copy access to variable values by using
|
||||
// GetterBytes functions registered in BuiltinVariable.
|
||||
func (vc *Context) EphemeralGet(name string) []byte {
|
||||
// 1. 先查自定义变量(需要转换为 []byte)
|
||||
if v, ok := vc.store[name]; ok {
|
||||
return []byte(v) // 注意:这里分配了,因为 store 是 string
|
||||
}
|
||||
|
||||
// 2. 查全局变量(需要转换为 []byte)
|
||||
if v, ok := GetGlobalVariable(name); ok {
|
||||
return []byte(v) // 注意:这里分配了,因为全局变量是 string
|
||||
}
|
||||
|
||||
// 3. 检查 bytesCache 缓存
|
||||
if v, ok := vc.bytesCache[name]; ok {
|
||||
return v
|
||||
}
|
||||
|
||||
// 4. 检查从 SetResponseInfo/SetServerName 设置的值
|
||||
// SAFETY: 这些值来自 struct 字段,在请求期间有效
|
||||
switch name {
|
||||
case VarStatus:
|
||||
if vc.status > 0 {
|
||||
b := []byte(strconv.Itoa(vc.status))
|
||||
vc.bytesCache[name] = b
|
||||
return b
|
||||
}
|
||||
if v := vc.ctx.UserValue(VarStatus); v != nil {
|
||||
if i, ok := v.(int); ok {
|
||||
b := []byte(strconv.Itoa(i))
|
||||
vc.bytesCache[name] = b
|
||||
return b
|
||||
}
|
||||
}
|
||||
case VarBodyBytesSent:
|
||||
if vc.bodySize > 0 {
|
||||
b := []byte(strconv.FormatInt(vc.bodySize, 10))
|
||||
vc.bytesCache[name] = b
|
||||
return b
|
||||
}
|
||||
if v := vc.ctx.UserValue(VarBodyBytesSent); v != nil {
|
||||
if i, ok := v.(int64); ok {
|
||||
b := []byte(strconv.FormatInt(i, 10))
|
||||
vc.bytesCache[name] = b
|
||||
return b
|
||||
}
|
||||
}
|
||||
b := []byte("0")
|
||||
vc.bytesCache[name] = b
|
||||
return b
|
||||
case VarRequestTime:
|
||||
if vc.duration > 0 {
|
||||
seconds := float64(vc.duration) / 1e9
|
||||
b := []byte(strconv.FormatFloat(seconds, 'f', 3, 64))
|
||||
vc.bytesCache[name] = b
|
||||
return b
|
||||
}
|
||||
if v := vc.ctx.UserValue(VarRequestTime); v != nil {
|
||||
if i, ok := v.(int64); ok {
|
||||
seconds := float64(i) / 1e9
|
||||
b := []byte(strconv.FormatFloat(seconds, 'f', 3, 64))
|
||||
vc.bytesCache[name] = b
|
||||
return b
|
||||
}
|
||||
}
|
||||
b := []byte("0.000")
|
||||
vc.bytesCache[name] = b
|
||||
return b
|
||||
case VarServerName:
|
||||
if vc.serverName != "" {
|
||||
return []byte(vc.serverName)
|
||||
}
|
||||
// 上游变量
|
||||
case VarUpstreamAddr:
|
||||
if vc.upstreamAddr != "" {
|
||||
return []byte(vc.upstreamAddr)
|
||||
}
|
||||
return []byte("-")
|
||||
case VarUpstreamStatus:
|
||||
if vc.upstreamStatus > 0 {
|
||||
b := []byte(strconv.Itoa(vc.upstreamStatus))
|
||||
vc.bytesCache[name] = b
|
||||
return b
|
||||
}
|
||||
return []byte("-")
|
||||
case VarUpstreamResponseTime:
|
||||
if vc.upstreamResponseTime > 0 {
|
||||
b := []byte(strconv.FormatFloat(vc.upstreamResponseTime, 'f', 3, 64))
|
||||
vc.bytesCache[name] = b
|
||||
return b
|
||||
}
|
||||
return []byte("-")
|
||||
case VarUpstreamConnectTime:
|
||||
if vc.upstreamConnectTime > 0 {
|
||||
b := []byte(strconv.FormatFloat(vc.upstreamConnectTime, 'f', 3, 64))
|
||||
vc.bytesCache[name] = b
|
||||
return b
|
||||
}
|
||||
return []byte("-")
|
||||
case VarUpstreamHeaderTime:
|
||||
if vc.upstreamHeaderTime > 0 {
|
||||
b := []byte(strconv.FormatFloat(vc.upstreamHeaderTime, 'f', 3, 64))
|
||||
vc.bytesCache[name] = b
|
||||
return b
|
||||
}
|
||||
return []byte("-")
|
||||
}
|
||||
|
||||
// 5. 使用 GetterBytes 求值内置变量(零拷贝)
|
||||
if b, ok := vc.evalBuiltinBytes(name); ok {
|
||||
vc.bytesCache[name] = b
|
||||
return b
|
||||
}
|
||||
|
||||
// 6. 如果只有 Getter,调用并转换为 []byte
|
||||
if v, ok := vc.evalBuiltin(name); ok {
|
||||
b := []byte(v)
|
||||
vc.bytesCache[name] = b
|
||||
return b
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PersistentGet returns string for cross-request storage.
|
||||
//
|
||||
// Use this method when you need to store the variable value beyond
|
||||
// the current request scope (e.g., in a database, cache, or long-lived struct).
|
||||
func (vc *Context) PersistentGet(name string) string {
|
||||
// 直接调用 Get,它返回 string
|
||||
v, _ := vc.Get(name)
|
||||
return v
|
||||
}
|
||||
|
||||
// evalBuiltinBytes 求值内置变量,返回 []byte(零拷贝)
|
||||
func (vc *Context) evalBuiltinBytes(name string) ([]byte, bool) {
|
||||
builtin := builtinVars[name]
|
||||
if builtin == nil || builtin.GetterBytes == nil {
|
||||
return nil, false
|
||||
}
|
||||
return builtin.GetterBytes(vc.ctx), true
|
||||
}
|
||||
|
||||
// Set 设置自定义变量
|
||||
func (vc *Context) Set(name string, value string) {
|
||||
vc.store[name] = value
|
||||
|
||||
@ -36,7 +36,7 @@ func BenchmarkVariableExpandSimple(b *testing.B) {
|
||||
template := "$remote_addr - $request_method"
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for b.Loop() {
|
||||
vc.Expand(template)
|
||||
}
|
||||
}
|
||||
@ -54,7 +54,7 @@ func BenchmarkVariableExpandComplex(b *testing.B) {
|
||||
template := "$remote_addr - [$time_local] \"$request_method $uri $args\" $status $body_bytes_sent"
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for b.Loop() {
|
||||
vc.Expand(template)
|
||||
}
|
||||
}
|
||||
@ -68,7 +68,7 @@ func BenchmarkVariableExpandMixed(b *testing.B) {
|
||||
template := "${remote_addr} - $request_method ${uri}?${args}"
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for b.Loop() {
|
||||
vc.Expand(template)
|
||||
}
|
||||
}
|
||||
@ -82,7 +82,7 @@ func BenchmarkVariableExpandNoVar(b *testing.B) {
|
||||
template := "This is a plain string with no variables"
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for b.Loop() {
|
||||
vc.Expand(template)
|
||||
}
|
||||
}
|
||||
@ -92,7 +92,7 @@ func BenchmarkVariableContextPool(b *testing.B) {
|
||||
ctx := setupBenchmarkRequestCtx()
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for b.Loop() {
|
||||
vc := NewContext(ctx)
|
||||
ReleaseContext(vc)
|
||||
}
|
||||
@ -122,7 +122,7 @@ func BenchmarkVariableGetCache(b *testing.B) {
|
||||
_, _ = vc.Get("remote_addr")
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for b.Loop() {
|
||||
vc.Get("remote_addr")
|
||||
}
|
||||
}
|
||||
@ -134,7 +134,7 @@ func BenchmarkVariableGetNoCache(b *testing.B) {
|
||||
ctx := setupBenchmarkRequestCtx()
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for b.Loop() {
|
||||
vc := NewContext(ctx)
|
||||
vc.Get("remote_addr")
|
||||
ReleaseContext(vc)
|
||||
@ -153,7 +153,7 @@ func BenchmarkVariableGetMultiple(b *testing.B) {
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for b.Loop() {
|
||||
for _, name := range vars {
|
||||
vc.Get(name)
|
||||
}
|
||||
@ -167,7 +167,7 @@ func BenchmarkVariableSetAndGet(b *testing.B) {
|
||||
defer ReleaseContext(vc)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for b.Loop() {
|
||||
vc.Set("custom_var", "custom_value")
|
||||
vc.Get("custom_var")
|
||||
}
|
||||
@ -188,7 +188,7 @@ func BenchmarkExpandStringStaticWithLookup(b *testing.B) {
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for b.Loop() {
|
||||
ExpandString(template, lookup)
|
||||
}
|
||||
}
|
||||
@ -206,7 +206,7 @@ func BenchmarkVariableExpandLongTemplate(b *testing.B) {
|
||||
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++ {
|
||||
for b.Loop() {
|
||||
vc.Expand(template)
|
||||
}
|
||||
}
|
||||
|
||||
@ -353,7 +353,7 @@ func BenchmarkExpandSimple(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
for b.Loop() {
|
||||
_ = vc.Expand(template)
|
||||
}
|
||||
}
|
||||
@ -374,7 +374,7 @@ func BenchmarkExpandComplex(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
for b.Loop() {
|
||||
_ = vc.Expand(template)
|
||||
}
|
||||
}
|
||||
@ -390,7 +390,7 @@ func BenchmarkExpandNoVariable(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
for b.Loop() {
|
||||
_ = vc.Expand(template)
|
||||
}
|
||||
}
|
||||
@ -409,7 +409,7 @@ func BenchmarkExpandBrace(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
for b.Loop() {
|
||||
_ = vc.Expand(template)
|
||||
}
|
||||
}
|
||||
@ -421,7 +421,7 @@ func BenchmarkPoolGetPut(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
for b.Loop() {
|
||||
vc := NewContext(ctx)
|
||||
ReleaseContext(vc)
|
||||
}
|
||||
@ -445,7 +445,7 @@ func BenchmarkExpandStringStatic(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
for b.Loop() {
|
||||
_ = ExpandString(template, lookup)
|
||||
}
|
||||
}
|
||||
@ -1029,7 +1029,7 @@ func BenchmarkUpstreamVariables(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
for b.Loop() {
|
||||
_, _ = vc.Get(VarUpstreamAddr)
|
||||
_, _ = vc.Get(VarUpstreamStatus)
|
||||
_, _ = vc.Get(VarUpstreamResponseTime)
|
||||
@ -1133,3 +1133,274 @@ func TestGlobalVariablesConcurrent(_ *testing.T) {
|
||||
<-done
|
||||
}
|
||||
}
|
||||
|
||||
// TestEphemeralGet 测试 EphemeralGet 方法
|
||||
func TestEphemeralGet(t *testing.T) {
|
||||
ctx := mockRequestCtx(t)
|
||||
vc := NewContext(ctx)
|
||||
defer ReleaseContext(vc)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
varName string
|
||||
expected []byte
|
||||
}{
|
||||
{"host", VarHost, []byte("example.com")},
|
||||
{"uri", VarURI, []byte("/test/path")},
|
||||
{"request_uri", VarRequestURI, []byte("/test/path?foo=bar&baz=qux")},
|
||||
{"args", VarArgs, []byte("foo=bar&baz=qux")},
|
||||
{"request_method", VarRequestMethod, []byte("GET")},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := vc.EphemeralGet(tt.varName)
|
||||
if string(result) != string(tt.expected) {
|
||||
t.Errorf("EphemeralGet(%q) = %q, want %q", tt.varName, result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestEphemeralGetCustomVariable 测试自定义变量的 EphemeralGet
|
||||
func TestEphemeralGetCustomVariable(t *testing.T) {
|
||||
ctx := mockRequestCtx(t)
|
||||
vc := NewContext(ctx)
|
||||
defer ReleaseContext(vc)
|
||||
|
||||
// 设置自定义变量
|
||||
vc.Set("custom_var", "custom_value")
|
||||
|
||||
result := vc.EphemeralGet("custom_var")
|
||||
if string(result) != "custom_value" {
|
||||
t.Errorf("EphemeralGet('custom_var') = %q, want 'custom_value'", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestEphemeralGetUndefined 测试未定义变量的 EphemeralGet
|
||||
func TestEphemeralGetUndefined(t *testing.T) {
|
||||
ctx := mockRequestCtx(t)
|
||||
vc := NewContext(ctx)
|
||||
defer ReleaseContext(vc)
|
||||
|
||||
result := vc.EphemeralGet("undefined_var")
|
||||
if result != nil {
|
||||
t.Errorf("EphemeralGet('undefined_var') = %q, want nil", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestEphemeralGetCache 测试 EphemeralGet 的缓存
|
||||
func TestEphemeralGetCache(t *testing.T) {
|
||||
ctx := mockRequestCtx(t)
|
||||
vc := NewContext(ctx)
|
||||
defer ReleaseContext(vc)
|
||||
|
||||
// 第一次获取
|
||||
result1 := vc.EphemeralGet(VarHost)
|
||||
// 第二次获取应该命中缓存
|
||||
result2 := vc.EphemeralGet(VarHost)
|
||||
|
||||
// 验证缓存返回相同的结果
|
||||
if string(result1) != string(result2) {
|
||||
t.Errorf("EphemeralGet cache: %q != %q", result1, result2)
|
||||
}
|
||||
}
|
||||
|
||||
// TestPersistentGet 测试 PersistentGet 方法
|
||||
func TestPersistentGet(t *testing.T) {
|
||||
ctx := mockRequestCtx(t)
|
||||
vc := NewContext(ctx)
|
||||
defer ReleaseContext(vc)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
varName string
|
||||
expected string
|
||||
}{
|
||||
{"host", VarHost, "example.com"},
|
||||
{"uri", VarURI, "/test/path"},
|
||||
{"request_method", VarRequestMethod, "GET"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := vc.PersistentGet(tt.varName)
|
||||
if result != tt.expected {
|
||||
t.Errorf("PersistentGet(%q) = %q, want %q", tt.varName, result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestPersistentGetUndefined 测试未定义变量的 PersistentGet
|
||||
func TestPersistentGetUndefined(t *testing.T) {
|
||||
ctx := mockRequestCtx(t)
|
||||
vc := NewContext(ctx)
|
||||
defer ReleaseContext(vc)
|
||||
|
||||
result := vc.PersistentGet("undefined_var")
|
||||
if result != "" {
|
||||
t.Errorf("PersistentGet('undefined_var') = %q, want empty string", result)
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetBackwardCompatibility 测试 Get 方法的向后兼容性
|
||||
func TestGetBackwardCompatibility(t *testing.T) {
|
||||
ctx := mockRequestCtx(t)
|
||||
vc := NewContext(ctx)
|
||||
defer ReleaseContext(vc)
|
||||
|
||||
// Get 应该返回与 PersistentGet 相同的结果
|
||||
tests := []string{VarHost, VarURI, VarRequestMethod, VarRequestURI}
|
||||
|
||||
for _, varName := range tests {
|
||||
getResult, ok := vc.Get(varName)
|
||||
if !ok {
|
||||
t.Errorf("Get(%q) returned not found", varName)
|
||||
continue
|
||||
}
|
||||
persistentResult := vc.PersistentGet(varName)
|
||||
if getResult != persistentResult {
|
||||
t.Errorf("Get(%q) = %q, PersistentGet(%q) = %q, should be equal",
|
||||
varName, getResult, varName, persistentResult)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestEphemeralGetUpstreamVariables 测试上游变量的 EphemeralGet
|
||||
func TestEphemeralGetUpstreamVariables(t *testing.T) {
|
||||
ctx := mockRequestCtx(t)
|
||||
vc := NewContext(ctx)
|
||||
defer ReleaseContext(vc)
|
||||
|
||||
// 未设置时应该返回默认值 "-"
|
||||
defaultTests := []struct {
|
||||
varName string
|
||||
expected []byte
|
||||
}{
|
||||
{VarUpstreamAddr, []byte("-")},
|
||||
{VarUpstreamStatus, []byte("-")},
|
||||
{VarUpstreamResponseTime, []byte("-")},
|
||||
}
|
||||
|
||||
for _, tt := range defaultTests {
|
||||
t.Run(tt.varName+"_default", func(t *testing.T) {
|
||||
result := vc.EphemeralGet(tt.varName)
|
||||
if string(result) != string(tt.expected) {
|
||||
t.Errorf("EphemeralGet(%q) = %q, want %q", tt.varName, result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 设置上游变量
|
||||
vc.SetUpstreamVars("http://backend:8080", 200, 0.123, 0.001, 0.045)
|
||||
|
||||
setTests := []struct {
|
||||
varName string
|
||||
expected []byte
|
||||
}{
|
||||
{VarUpstreamAddr, []byte("http://backend:8080")},
|
||||
{VarUpstreamStatus, []byte("200")},
|
||||
{VarUpstreamResponseTime, []byte("0.123")},
|
||||
}
|
||||
|
||||
for _, tt := range setTests {
|
||||
t.Run(tt.varName+"_set", func(t *testing.T) {
|
||||
result := vc.EphemeralGet(tt.varName)
|
||||
if string(result) != string(tt.expected) {
|
||||
t.Errorf("EphemeralGet(%q) = %q, want %q", tt.varName, result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestEphemeralGetResponseInfo 测试响应信息的 EphemeralGet
|
||||
func TestEphemeralGetResponseInfo(t *testing.T) {
|
||||
ctx := mockRequestCtx(t)
|
||||
vc := NewContext(ctx)
|
||||
defer ReleaseContext(vc)
|
||||
|
||||
// 设置响应信息
|
||||
vc.SetResponseInfo(200, 1024, 15000000) // 15ms
|
||||
|
||||
tests := []struct {
|
||||
varName string
|
||||
expected []byte
|
||||
}{
|
||||
{VarStatus, []byte("200")},
|
||||
{VarBodyBytesSent, []byte("1024")},
|
||||
{VarRequestTime, []byte("0.015")},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.varName, func(t *testing.T) {
|
||||
result := vc.EphemeralGet(tt.varName)
|
||||
if string(result) != string(tt.expected) {
|
||||
t.Errorf("EphemeralGet(%q) = %q, want %q", tt.varName, result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkEphemeralGet 基准测试:EphemeralGet 零拷贝性能
|
||||
func BenchmarkEphemeralGet(b *testing.B) {
|
||||
ctx := setupBenchmarkRequestCtx()
|
||||
vc := NewContext(ctx)
|
||||
defer ReleaseContext(vc)
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for b.Loop() {
|
||||
_ = vc.EphemeralGet(VarHost)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkEphemeralGetCached 基准测试:EphemeralGet 缓存命中性能
|
||||
func BenchmarkEphemeralGetCached(b *testing.B) {
|
||||
ctx := setupBenchmarkRequestCtx()
|
||||
vc := NewContext(ctx)
|
||||
defer ReleaseContext(vc)
|
||||
|
||||
// 预热缓存
|
||||
_ = vc.EphemeralGet(VarHost)
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for b.Loop() {
|
||||
_ = vc.EphemeralGet(VarHost)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkPersistentGet 基准测试:PersistentGet 性能
|
||||
func BenchmarkPersistentGet(b *testing.B) {
|
||||
ctx := setupBenchmarkRequestCtx()
|
||||
vc := NewContext(ctx)
|
||||
defer ReleaseContext(vc)
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for b.Loop() {
|
||||
_ = vc.PersistentGet(VarHost)
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkEphemeralGetMultiple 基准测试:获取多个变量的 EphemeralGet
|
||||
func BenchmarkEphemeralGetMultiple(b *testing.B) {
|
||||
ctx := setupBenchmarkRequestCtx()
|
||||
vc := NewContext(ctx)
|
||||
defer ReleaseContext(vc)
|
||||
|
||||
vars := []string{VarHost, VarURI, VarRequestMethod, VarRequestURI, VarArgs}
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for b.Loop() {
|
||||
for _, name := range vars {
|
||||
_ = vc.EphemeralGet(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user