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:
xfy 2026-04-16 13:50:15 +08:00
parent ddc9e0cef1
commit dc7358bf4e
4 changed files with 468 additions and 21 deletions

View File

@ -49,6 +49,10 @@ func init() {
Getter: func(ctx *fasthttp.RequestCtx) string { Getter: func(ctx *fasthttp.RequestCtx) string {
return string(ctx.Host()) 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 // 2. $remote_addr - 客户端 IP
@ -91,6 +95,10 @@ func init() {
Getter: func(ctx *fasthttp.RequestCtx) string { Getter: func(ctx *fasthttp.RequestCtx) string {
return string(ctx.RequestURI()) 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 路径 // 5. $uri - 解码后的 URI 路径
@ -100,6 +108,10 @@ func init() {
Getter: func(ctx *fasthttp.RequestCtx) string { Getter: func(ctx *fasthttp.RequestCtx) string {
return string(ctx.Path()) return string(ctx.Path())
}, },
GetterBytes: func(ctx *fasthttp.RequestCtx) []byte {
// SAFETY: ctx.Path() returns []byte valid within request scope
return ctx.Path()
},
}) })
// 6. $args - 查询参数字符串 // 6. $args - 查询参数字符串
@ -109,6 +121,10 @@ func init() {
Getter: func(ctx *fasthttp.RequestCtx) string { Getter: func(ctx *fasthttp.RequestCtx) string {
return string(ctx.QueryArgs().QueryString()) 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 - 请求方法 // 7. $request_method - 请求方法
@ -118,6 +134,10 @@ func init() {
Getter: func(ctx *fasthttp.RequestCtx) string { Getter: func(ctx *fasthttp.RequestCtx) string {
return string(ctx.Method()) return string(ctx.Method())
}, },
GetterBytes: func(ctx *fasthttp.RequestCtx) []byte {
// SAFETY: ctx.Method() returns []byte valid within request scope
return ctx.Method()
},
}) })
// 8. $scheme - 协议 // 8. $scheme - 协议

View File

@ -29,6 +29,7 @@ import (
// BuiltinVariable 内置变量定义 // BuiltinVariable 内置变量定义
type BuiltinVariable struct { type BuiltinVariable struct {
Getter func(ctx *fasthttp.RequestCtx) string Getter func(ctx *fasthttp.RequestCtx) string
GetterBytes func(ctx *fasthttp.RequestCtx) []byte // 零拷贝 getter用于 EphemeralGet
Name string Name string
Description string Description string
} }
@ -37,7 +38,8 @@ type BuiltinVariable struct {
type Context struct { type Context struct {
ctx *fasthttp.RequestCtx ctx *fasthttp.RequestCtx
store map[string]string store map[string]string
cache map[string]string cache map[string]string // string 缓存(用于 PersistentGet
bytesCache map[string][]byte // []byte 缓存(用于 EphemeralGet
serverName string serverName string
upstreamAddr string upstreamAddr string
status int status int
@ -53,8 +55,9 @@ type Context struct {
var pool = sync.Pool{ var pool = sync.Pool{
New: func() interface{} { New: func() interface{} {
return &Context{ return &Context{
store: make(map[string]string), store: make(map[string]string),
cache: 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 { for k := range vc.cache {
delete(vc.cache, k) delete(vc.cache, k)
} }
// 清空内置变量 bytes 缓存
for k := range vc.bytesCache {
delete(vc.bytesCache, k)
}
// 清空自定义变量 store // 清空自定义变量 store
for k := range vc.store { for k := range vc.store {
delete(vc.store, k) delete(vc.store, k)
@ -272,6 +279,155 @@ func (vc *Context) Get(name string) (string, bool) {
return "", false 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 设置自定义变量 // Set 设置自定义变量
func (vc *Context) Set(name string, value string) { func (vc *Context) Set(name string, value string) {
vc.store[name] = value vc.store[name] = value

View File

@ -36,7 +36,7 @@ func BenchmarkVariableExpandSimple(b *testing.B) {
template := "$remote_addr - $request_method" template := "$remote_addr - $request_method"
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for b.Loop() {
vc.Expand(template) 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" template := "$remote_addr - [$time_local] \"$request_method $uri $args\" $status $body_bytes_sent"
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for b.Loop() {
vc.Expand(template) vc.Expand(template)
} }
} }
@ -68,7 +68,7 @@ func BenchmarkVariableExpandMixed(b *testing.B) {
template := "${remote_addr} - $request_method ${uri}?${args}" template := "${remote_addr} - $request_method ${uri}?${args}"
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for b.Loop() {
vc.Expand(template) vc.Expand(template)
} }
} }
@ -82,7 +82,7 @@ func BenchmarkVariableExpandNoVar(b *testing.B) {
template := "This is a plain string with no variables" template := "This is a plain string with no variables"
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for b.Loop() {
vc.Expand(template) vc.Expand(template)
} }
} }
@ -92,7 +92,7 @@ func BenchmarkVariableContextPool(b *testing.B) {
ctx := setupBenchmarkRequestCtx() ctx := setupBenchmarkRequestCtx()
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for b.Loop() {
vc := NewContext(ctx) vc := NewContext(ctx)
ReleaseContext(vc) ReleaseContext(vc)
} }
@ -122,7 +122,7 @@ func BenchmarkVariableGetCache(b *testing.B) {
_, _ = vc.Get("remote_addr") _, _ = vc.Get("remote_addr")
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for b.Loop() {
vc.Get("remote_addr") vc.Get("remote_addr")
} }
} }
@ -134,7 +134,7 @@ func BenchmarkVariableGetNoCache(b *testing.B) {
ctx := setupBenchmarkRequestCtx() ctx := setupBenchmarkRequestCtx()
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for b.Loop() {
vc := NewContext(ctx) vc := NewContext(ctx)
vc.Get("remote_addr") vc.Get("remote_addr")
ReleaseContext(vc) ReleaseContext(vc)
@ -153,7 +153,7 @@ func BenchmarkVariableGetMultiple(b *testing.B) {
} }
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for b.Loop() {
for _, name := range vars { for _, name := range vars {
vc.Get(name) vc.Get(name)
} }
@ -167,7 +167,7 @@ func BenchmarkVariableSetAndGet(b *testing.B) {
defer ReleaseContext(vc) defer ReleaseContext(vc)
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for b.Loop() {
vc.Set("custom_var", "custom_value") vc.Set("custom_var", "custom_value")
vc.Get("custom_var") vc.Get("custom_var")
} }
@ -188,7 +188,7 @@ func BenchmarkExpandStringStaticWithLookup(b *testing.B) {
} }
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for b.Loop() {
ExpandString(template, lookup) 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" template := "$remote_addr - [$time_local] \"$request_method $uri?$args\" $status $body_bytes_sent \"$http_referer\" \"$http_user_agent\" $request_time $server_name"
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for b.Loop() {
vc.Expand(template) vc.Expand(template)
} }
} }

View File

@ -353,7 +353,7 @@ func BenchmarkExpandSimple(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.ReportAllocs() b.ReportAllocs()
for i := 0; i < b.N; i++ { for b.Loop() {
_ = vc.Expand(template) _ = vc.Expand(template)
} }
} }
@ -374,7 +374,7 @@ func BenchmarkExpandComplex(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.ReportAllocs() b.ReportAllocs()
for i := 0; i < b.N; i++ { for b.Loop() {
_ = vc.Expand(template) _ = vc.Expand(template)
} }
} }
@ -390,7 +390,7 @@ func BenchmarkExpandNoVariable(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.ReportAllocs() b.ReportAllocs()
for i := 0; i < b.N; i++ { for b.Loop() {
_ = vc.Expand(template) _ = vc.Expand(template)
} }
} }
@ -409,7 +409,7 @@ func BenchmarkExpandBrace(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.ReportAllocs() b.ReportAllocs()
for i := 0; i < b.N; i++ { for b.Loop() {
_ = vc.Expand(template) _ = vc.Expand(template)
} }
} }
@ -421,7 +421,7 @@ func BenchmarkPoolGetPut(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.ReportAllocs() b.ReportAllocs()
for i := 0; i < b.N; i++ { for b.Loop() {
vc := NewContext(ctx) vc := NewContext(ctx)
ReleaseContext(vc) ReleaseContext(vc)
} }
@ -445,7 +445,7 @@ func BenchmarkExpandStringStatic(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.ReportAllocs() b.ReportAllocs()
for i := 0; i < b.N; i++ { for b.Loop() {
_ = ExpandString(template, lookup) _ = ExpandString(template, lookup)
} }
} }
@ -1029,7 +1029,7 @@ func BenchmarkUpstreamVariables(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.ReportAllocs() b.ReportAllocs()
for i := 0; i < b.N; i++ { for b.Loop() {
_, _ = vc.Get(VarUpstreamAddr) _, _ = vc.Get(VarUpstreamAddr)
_, _ = vc.Get(VarUpstreamStatus) _, _ = vc.Get(VarUpstreamStatus)
_, _ = vc.Get(VarUpstreamResponseTime) _, _ = vc.Get(VarUpstreamResponseTime)
@ -1133,3 +1133,274 @@ func TestGlobalVariablesConcurrent(_ *testing.T) {
<-done <-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)
}
}
}