refactor: remove unused code and fix formatting
- Remove unused benchmark/tools package - Make ValidAlgorithms private (validAlgorithms) in loadbalance - Remove dead code (_ = result) in lua/api_socket_tcp.go - Fix code formatting with goimports Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
ce80352e79
commit
25d93c25fa
2
go.mod
2
go.mod
@ -5,6 +5,7 @@ go 1.26.1
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.2.1
|
||||
github.com/fasthttp/router v1.5.4
|
||||
github.com/goccy/go-json v0.10.4
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||
@ -41,7 +42,6 @@ require (
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/goccy/go-json v0.10.4 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/magiconair/properties v1.8.10 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
|
||||
@ -1,42 +0,0 @@
|
||||
<!-- Parent: ../AGENTS.md -->
|
||||
<!-- Generated: 2026-04-07 | Updated: 2026-04-07 -->
|
||||
|
||||
# tools
|
||||
|
||||
## Purpose
|
||||
基准测试工具集,提供 Mock 后端服务器、负载生成器和测试数据生成功能,支持各种测试场景(延迟、错误、随机响应)。
|
||||
|
||||
## Key Files
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| `mock_backend.go` | Mock 后端实现,支持多种响应模式(固定、延迟、错误、随机) |
|
||||
| `loadgen.go` | 负载生成器,收集 QPS、延迟分布(P50/P90/P99)统计 |
|
||||
| `testdata.go` | 测试数据生成,支持多种预定义大小(1KB~10MB) |
|
||||
|
||||
## For AI Agents
|
||||
|
||||
### Working In This Directory
|
||||
- Mock 后端使用 `fasthttputil.InmemoryListener` 进行零网络开销测试
|
||||
- `BackendMode` 支持四种模式:ModeFixed、ModeDelay、ModeError、ModeRandomResponse
|
||||
- 负载生成器支持并发执行和百分位延迟统计
|
||||
- 测试数据使用随机字节填充,适合压缩测试
|
||||
|
||||
### Testing Requirements
|
||||
- 工具本身无独立测试,作为其他模块基准测试的基础设施
|
||||
- 使用示例见 `internal/proxy/proxy_bench_test.go`
|
||||
|
||||
### Common Patterns
|
||||
- 创建简单后端:`SimpleMockBackend(statusCode, body)`
|
||||
- 创建延迟后端:`DelayedMockBackend(delay, body)`
|
||||
- 创建错误后端:`ErrorMockBackend(errorRate, body)`
|
||||
- 创建加权目标:`CreateWeightedTestTargets(n)`
|
||||
- 运行负载测试:`loadGen.Run(n, concurrency)`
|
||||
|
||||
## Dependencies
|
||||
|
||||
### External
|
||||
- `github.com/valyala/fasthttp` - HTTP 框架
|
||||
- `github.com/valyala/fasthttp/fasthttputil` - 内存监听器
|
||||
|
||||
<!-- MANUAL: -->
|
||||
@ -1,359 +0,0 @@
|
||||
// Package tools 提供基准测试工具函数。
|
||||
//
|
||||
// 该文件提供标准化的基准测试上下文构造器,用于:
|
||||
// - 快速创建模拟的 fasthttp.RequestCtx
|
||||
// - 标准化测试数据大小
|
||||
// - 简化组件级基准测试编写
|
||||
//
|
||||
// 作者:xfy
|
||||
package tools
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
// BenchmarkContext 提供标准化的基准测试上下文。
|
||||
//
|
||||
// 包含构造模拟请求所需的所有配置。
|
||||
type BenchmarkContext struct {
|
||||
// randSrc 随机数生成器
|
||||
randSrc *rand.Rand
|
||||
|
||||
// RequestSize 请求数据大小
|
||||
RequestSize TestDataSize
|
||||
|
||||
// ResponseSize 响应数据大小
|
||||
ResponseSize TestDataSize
|
||||
|
||||
// Concurrency 并发级别(用于并行测试)
|
||||
Concurrency int
|
||||
}
|
||||
|
||||
// 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(_, _ 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 range size {
|
||||
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:
|
||||
// 池已满,丢弃
|
||||
}
|
||||
}
|
||||
@ -1,160 +0,0 @@
|
||||
// Package tools provides testing utilities and mock infrastructure for benchmark tests.
|
||||
package tools
|
||||
|
||||
import (
|
||||
"math"
|
||||
"slices"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
// FasthttpLoadGenerator is a load generator using fasthttp client.
|
||||
type FasthttpLoadGenerator struct {
|
||||
client *fasthttp.HostClient
|
||||
addr string
|
||||
stats LoadGenStats
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// LoadGenStats contains load generator statistics.
|
||||
type LoadGenStats struct {
|
||||
Latencies []time.Duration // For percentile calculation
|
||||
TotalRequests int
|
||||
SuccessCount int
|
||||
ErrorCount int
|
||||
TotalDuration time.Duration
|
||||
MinLatency time.Duration
|
||||
MaxLatency time.Duration
|
||||
MeanLatency time.Duration
|
||||
P50Latency time.Duration
|
||||
P90Latency time.Duration
|
||||
P99Latency time.Duration
|
||||
QPS float64
|
||||
}
|
||||
|
||||
// NewFasthttpLoadGenerator creates a new load generator for the given address.
|
||||
func NewFasthttpLoadGenerator(addr string) *FasthttpLoadGenerator {
|
||||
return &FasthttpLoadGenerator{
|
||||
client: &fasthttp.HostClient{
|
||||
Addr: addr,
|
||||
MaxConns: 1000,
|
||||
},
|
||||
addr: addr,
|
||||
stats: LoadGenStats{
|
||||
MinLatency: time.Duration(math.MaxInt64),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Run executes a load test with n requests using the specified concurrency.
|
||||
// Returns collected statistics.
|
||||
func (lg *FasthttpLoadGenerator) Run(n int, concurrency int) *LoadGenStats {
|
||||
var wg sync.WaitGroup
|
||||
requestsPerWorker := n / concurrency
|
||||
|
||||
// Channels for collecting metrics
|
||||
latencyChan := make(chan time.Duration, n)
|
||||
errorChan := make(chan error, n)
|
||||
|
||||
start := time.Now()
|
||||
|
||||
for range concurrency {
|
||||
wg.Go(func() {
|
||||
req := fasthttp.AcquireRequest()
|
||||
resp := fasthttp.AcquireResponse()
|
||||
defer fasthttp.ReleaseRequest(req)
|
||||
defer fasthttp.ReleaseResponse(resp)
|
||||
|
||||
for range requestsPerWorker {
|
||||
req.SetRequestURI("http://" + lg.addr + "/")
|
||||
req.Header.SetMethod("GET")
|
||||
|
||||
reqStart := time.Now()
|
||||
err := lg.client.Do(req, resp)
|
||||
latency := time.Since(reqStart)
|
||||
|
||||
latencyChan <- latency
|
||||
if err != nil {
|
||||
errorChan <- err
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
close(latencyChan)
|
||||
close(errorChan)
|
||||
|
||||
totalDuration := time.Since(start)
|
||||
|
||||
// Collect latencies
|
||||
latencies := make([]time.Duration, 0, n)
|
||||
for lat := range latencyChan {
|
||||
latencies = append(latencies, lat)
|
||||
}
|
||||
|
||||
// Count errors
|
||||
errorCount := 0
|
||||
for err := range errorChan {
|
||||
_ = err // Error recorded, used for counting
|
||||
errorCount++
|
||||
}
|
||||
|
||||
// Calculate statistics
|
||||
lg.mu.Lock()
|
||||
defer lg.mu.Unlock()
|
||||
|
||||
lg.stats.TotalRequests = n
|
||||
lg.stats.ErrorCount = errorCount
|
||||
lg.stats.SuccessCount = n - errorCount
|
||||
lg.stats.TotalDuration = totalDuration
|
||||
lg.stats.QPS = float64(n) / totalDuration.Seconds()
|
||||
lg.stats.Latencies = latencies
|
||||
|
||||
// Calculate latency distribution
|
||||
if len(latencies) > 0 {
|
||||
slices.Sort(latencies)
|
||||
|
||||
lg.stats.MinLatency = latencies[0]
|
||||
lg.stats.MaxLatency = latencies[len(latencies)-1]
|
||||
|
||||
// Calculate mean
|
||||
var sum time.Duration
|
||||
for _, l := range latencies {
|
||||
sum += l
|
||||
}
|
||||
lg.stats.MeanLatency = sum / time.Duration(len(latencies))
|
||||
|
||||
// Calculate percentiles
|
||||
lg.stats.P50Latency = latencies[len(latencies)*50/100]
|
||||
lg.stats.P90Latency = latencies[len(latencies)*90/100]
|
||||
lg.stats.P99Latency = latencies[len(latencies)*99/100]
|
||||
}
|
||||
|
||||
return &lg.stats
|
||||
}
|
||||
|
||||
// GetStats returns current statistics without running a test.
|
||||
func (lg *FasthttpLoadGenerator) GetStats() *LoadGenStats {
|
||||
lg.mu.Lock()
|
||||
defer lg.mu.Unlock()
|
||||
return &lg.stats
|
||||
}
|
||||
|
||||
// RunParallel runs requests in parallel using testing.PB.
|
||||
// This is designed for use with Go benchmark functions.
|
||||
func (lg *FasthttpLoadGenerator) RunParallel(pb *testing.PB) {
|
||||
req := fasthttp.AcquireRequest()
|
||||
resp := fasthttp.AcquireResponse()
|
||||
defer fasthttp.ReleaseRequest(req)
|
||||
defer fasthttp.ReleaseResponse(resp)
|
||||
|
||||
for pb.Next() {
|
||||
req.SetRequestURI("http://" + lg.addr + "/")
|
||||
req.Header.SetMethod("GET")
|
||||
_ = lg.client.Do(req, resp)
|
||||
}
|
||||
}
|
||||
@ -1,151 +0,0 @@
|
||||
// Package tools provides testing utilities and mock infrastructure for benchmark tests.
|
||||
package tools
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/valyala/fasthttp"
|
||||
"github.com/valyala/fasthttp/fasthttputil"
|
||||
)
|
||||
|
||||
// BackendMode defines the response behavior of the mock backend.
|
||||
type BackendMode int
|
||||
|
||||
const (
|
||||
// ModeFixed returns a fixed response with configurable status and body.
|
||||
ModeFixed BackendMode = iota
|
||||
// ModeDelay adds artificial delay before responding.
|
||||
ModeDelay
|
||||
// ModeError returns errors for a percentage of requests.
|
||||
ModeError
|
||||
// ModeRandomResponse returns random status codes.
|
||||
ModeRandomResponse
|
||||
)
|
||||
|
||||
// MockBackendConfig configures the mock backend behavior.
|
||||
type MockBackendConfig struct {
|
||||
Body []byte
|
||||
Mode BackendMode
|
||||
StatusCode int
|
||||
Delay time.Duration
|
||||
ErrorRate float64 // 0.0 to 1.0, for ModeError
|
||||
}
|
||||
|
||||
// MockBackend represents a mock fasthttp server for testing.
|
||||
type MockBackend struct {
|
||||
server *fasthttp.Server
|
||||
config MockBackendConfig
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// StartMockFasthttpBackend starts a mock fasthttp backend server.
|
||||
// Returns the server address and a cleanup function.
|
||||
func StartMockFasthttpBackend(config MockBackendConfig) (string, func()) {
|
||||
mb := &MockBackend{
|
||||
config: config,
|
||||
}
|
||||
|
||||
mb.server = &fasthttp.Server{
|
||||
Handler: mb.handler,
|
||||
}
|
||||
|
||||
// Use in-memory listener for testing
|
||||
ln := fasthttputil.NewInmemoryListener()
|
||||
|
||||
// Start server in background
|
||||
go func() {
|
||||
_ = mb.server.Serve(ln)
|
||||
}()
|
||||
|
||||
addr := "127.0.0.1:0" // In-memory listener address
|
||||
|
||||
cleanup := func() {
|
||||
_ = mb.server.Shutdown()
|
||||
_ = ln.Close()
|
||||
}
|
||||
|
||||
return addr, cleanup
|
||||
}
|
||||
|
||||
// handler processes incoming requests based on the configured mode.
|
||||
func (mb *MockBackend) handler(ctx *fasthttp.RequestCtx) {
|
||||
mb.mu.RLock()
|
||||
config := mb.config
|
||||
mb.mu.RUnlock()
|
||||
|
||||
switch config.Mode {
|
||||
case ModeFixed:
|
||||
ctx.SetStatusCode(config.StatusCode)
|
||||
_, _ = ctx.Write(config.Body)
|
||||
|
||||
case ModeDelay:
|
||||
time.Sleep(config.Delay)
|
||||
ctx.SetStatusCode(config.StatusCode)
|
||||
_, _ = ctx.Write(config.Body)
|
||||
|
||||
case ModeError:
|
||||
if rand.Float64() < config.ErrorRate {
|
||||
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
|
||||
_, _ = ctx.WriteString("internal server error")
|
||||
return
|
||||
}
|
||||
ctx.SetStatusCode(config.StatusCode)
|
||||
_, _ = ctx.Write(config.Body)
|
||||
|
||||
case ModeRandomResponse:
|
||||
codes := []int{
|
||||
fasthttp.StatusOK,
|
||||
fasthttp.StatusCreated,
|
||||
fasthttp.StatusNoContent,
|
||||
fasthttp.StatusBadRequest,
|
||||
fasthttp.StatusNotFound,
|
||||
}
|
||||
ctx.SetStatusCode(codes[rand.Intn(len(codes))])
|
||||
_, _ = ctx.Write(config.Body)
|
||||
|
||||
default: // ModeFixed
|
||||
ctx.SetStatusCode(config.StatusCode)
|
||||
_, _ = ctx.Write(config.Body)
|
||||
}
|
||||
}
|
||||
|
||||
// SetConfig updates the backend configuration at runtime.
|
||||
func (mb *MockBackend) SetConfig(config MockBackendConfig) {
|
||||
mb.mu.Lock()
|
||||
defer mb.mu.Unlock()
|
||||
mb.config = config
|
||||
}
|
||||
|
||||
// SimpleMockBackend creates a simple backend with fixed response.
|
||||
// Returns address and cleanup function.
|
||||
func SimpleMockBackend(statusCode int, body []byte) (string, func()) {
|
||||
return StartMockFasthttpBackend(MockBackendConfig{
|
||||
Mode: ModeFixed,
|
||||
StatusCode: statusCode,
|
||||
Body: body,
|
||||
})
|
||||
}
|
||||
|
||||
// DelayedMockBackend creates a backend with delayed responses.
|
||||
// Returns address and cleanup function.
|
||||
func DelayedMockBackend(delay time.Duration, body []byte) (string, func()) {
|
||||
return StartMockFasthttpBackend(MockBackendConfig{
|
||||
Mode: ModeDelay,
|
||||
StatusCode: fasthttp.StatusOK,
|
||||
Body: body,
|
||||
Delay: delay,
|
||||
})
|
||||
}
|
||||
|
||||
// ErrorMockBackend creates a backend that returns errors at the specified rate.
|
||||
// Returns address and cleanup function.
|
||||
func ErrorMockBackend(errorRate float64, body []byte) (string, func()) {
|
||||
return StartMockFasthttpBackend(MockBackendConfig{
|
||||
Mode: ModeError,
|
||||
StatusCode: fasthttp.StatusOK,
|
||||
Body: body,
|
||||
ErrorRate: errorRate,
|
||||
})
|
||||
}
|
||||
@ -1,182 +0,0 @@
|
||||
// Package tools provides testing utilities and mock infrastructure for benchmark tests.
|
||||
package tools
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestDataSize represents predefined test data sizes.
|
||||
type TestDataSize int
|
||||
|
||||
const (
|
||||
// Size1KB represents 1KB test data.
|
||||
Size1KB TestDataSize = 1024
|
||||
// Size10KB represents 10KB test data.
|
||||
Size10KB TestDataSize = 10 * 1024
|
||||
// Size100KB represents 100KB test data.
|
||||
Size100KB TestDataSize = 100 * 1024
|
||||
// Size1MB represents 1MB test data.
|
||||
Size1MB TestDataSize = 1024 * 1024
|
||||
// Size10MB represents 10MB test data.
|
||||
Size10MB TestDataSize = 10 * 1024 * 1024
|
||||
)
|
||||
|
||||
// GenerateTestData generates test data of the specified size.
|
||||
func GenerateTestData(size TestDataSize) []byte {
|
||||
data := make([]byte, size)
|
||||
// Fill with random data for compression testing
|
||||
for i := range data {
|
||||
data[i] = byte(rand.Intn(256))
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// GenerateTestDataString generates test data as string.
|
||||
func GenerateTestDataString(size TestDataSize) string {
|
||||
return string(GenerateTestData(size))
|
||||
}
|
||||
|
||||
// TestTarget represents a backend target for testing.
|
||||
type TestTarget struct {
|
||||
Address string
|
||||
Weight int
|
||||
}
|
||||
|
||||
// createTestTargets creates n test backend targets with mock servers.
|
||||
// Returns the target configurations and a cleanup function.
|
||||
func createTestTargets(n int) ([]TestTarget, func()) {
|
||||
targets := make([]TestTarget, n)
|
||||
cleanups := make([]func(), n)
|
||||
|
||||
for i := range n {
|
||||
body := GenerateTestData(Size1KB)
|
||||
addr, cleanup := SimpleMockBackend(200, body)
|
||||
targets[i] = TestTarget{
|
||||
Address: addr,
|
||||
Weight: 1,
|
||||
}
|
||||
cleanups[i] = cleanup
|
||||
}
|
||||
|
||||
cleanupAll := func() {
|
||||
for _, cleanup := range cleanups {
|
||||
if cleanup != nil {
|
||||
cleanup()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return targets, cleanupAll
|
||||
}
|
||||
|
||||
// CreateTestTargets creates n test backend targets.
|
||||
// Returns the target configurations and a cleanup function.
|
||||
func CreateTestTargets(n int) ([]TestTarget, func()) {
|
||||
return createTestTargets(n)
|
||||
}
|
||||
|
||||
// CreateWeightedTestTargets creates n test backend targets with varying weights.
|
||||
// Returns the target configurations and a cleanup function.
|
||||
func CreateWeightedTestTargets(n int) ([]TestTarget, func()) {
|
||||
targets := make([]TestTarget, n)
|
||||
cleanups := make([]func(), n)
|
||||
|
||||
for i := range n {
|
||||
body := GenerateTestData(Size1KB)
|
||||
addr, cleanup := SimpleMockBackend(200, body)
|
||||
// Vary weights: 1, 2, 3, etc.
|
||||
targets[i] = TestTarget{
|
||||
Address: addr,
|
||||
Weight: i + 1,
|
||||
}
|
||||
cleanups[i] = cleanup
|
||||
}
|
||||
|
||||
cleanupAll := func() {
|
||||
for _, cleanup := range cleanups {
|
||||
if cleanup != nil {
|
||||
cleanup()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return targets, cleanupAll
|
||||
}
|
||||
|
||||
// CreateDelayedTestTargets creates n test backend targets with varying delays.
|
||||
// Useful for testing timeout and latency-related behavior.
|
||||
func CreateDelayedTestTargets(n int, baseDelay time.Duration) ([]TestTarget, func()) {
|
||||
targets := make([]TestTarget, n)
|
||||
cleanups := make([]func(), n)
|
||||
|
||||
for i := range n {
|
||||
body := GenerateTestData(Size1KB)
|
||||
// Each target has increasing delay
|
||||
delay := baseDelay * time.Duration(i+1)
|
||||
addr, cleanup := DelayedMockBackend(delay, body)
|
||||
targets[i] = TestTarget{
|
||||
Address: addr,
|
||||
Weight: 1,
|
||||
}
|
||||
cleanups[i] = cleanup
|
||||
}
|
||||
|
||||
cleanupAll := func() {
|
||||
for _, cleanup := range cleanups {
|
||||
if cleanup != nil {
|
||||
cleanup()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return targets, cleanupAll
|
||||
}
|
||||
|
||||
// CreateErrorTestTargets creates n test backend targets with varying error rates.
|
||||
// Useful for testing error handling and circuit breaker behavior.
|
||||
func CreateErrorTestTargets(n int, baseErrorRate float64) ([]TestTarget, func()) {
|
||||
targets := make([]TestTarget, n)
|
||||
cleanups := make([]func(), n)
|
||||
|
||||
for i := range n {
|
||||
body := GenerateTestData(Size1KB)
|
||||
// Vary error rates slightly per target
|
||||
errorRate := baseErrorRate + float64(i)*0.05
|
||||
if errorRate > 1.0 {
|
||||
errorRate = 1.0
|
||||
}
|
||||
addr, cleanup := ErrorMockBackend(errorRate, body)
|
||||
targets[i] = TestTarget{
|
||||
Address: addr,
|
||||
Weight: 1,
|
||||
}
|
||||
cleanups[i] = cleanup
|
||||
}
|
||||
|
||||
cleanupAll := func() {
|
||||
for _, cleanup := range cleanups {
|
||||
if cleanup != nil {
|
||||
cleanup()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return targets, cleanupAll
|
||||
}
|
||||
|
||||
// GenerateCacheKey generates a unique cache key for testing.
|
||||
func GenerateCacheKey(prefix string, index int) string {
|
||||
return fmt.Sprintf("%s-key-%d", prefix, index)
|
||||
}
|
||||
|
||||
// GenerateCacheValue generates a cache value of specified size.
|
||||
func GenerateCacheValue(size TestDataSize) []byte {
|
||||
return GenerateTestData(size)
|
||||
}
|
||||
|
||||
// GenerateRandomCacheKey generates a random cache key.
|
||||
func GenerateRandomCacheKey() string {
|
||||
return fmt.Sprintf("random-key-%d-%d", time.Now().UnixNano(), rand.Int())
|
||||
}
|
||||
@ -69,8 +69,8 @@ func DefaultConfig() *Config {
|
||||
CodeCacheSize: 1000,
|
||||
EnableFileWatch: true,
|
||||
MaxExecutionTime: 30 * time.Second,
|
||||
LStatePoolInitialSize: 100,
|
||||
LStatePoolMaxSize: 1000,
|
||||
LStatePoolInitialSize: 100,
|
||||
LStatePoolMaxSize: 1000,
|
||||
},
|
||||
},
|
||||
Static: []StaticConfig{{
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
// Package loadbalance 提供负载均衡算法实现。
|
||||
//
|
||||
// 该文件包含负载均衡算法相关的辅助逻辑,包括:
|
||||
// - ValidAlgorithms 有效算法列表定义
|
||||
// - IsValidAlgorithm 算法有效性校验函数
|
||||
//
|
||||
// 主要用途:
|
||||
@ -9,7 +8,7 @@
|
||||
// 用于校验和枚举系统支持的负载均衡算法类型,供配置解析和路由选择使用。
|
||||
//
|
||||
// 注意事项:
|
||||
// - 新增算法时必须同步更新 ValidAlgorithms 列表
|
||||
// - 新增算法时必须同步更新 validAlgorithms 列表
|
||||
// - 空字符串被视为有效值,表示使用默认算法
|
||||
//
|
||||
// 作者:xfy
|
||||
@ -17,15 +16,8 @@ package loadbalance
|
||||
|
||||
import "slices"
|
||||
|
||||
// ValidAlgorithms 定义系统支持的负载均衡算法名称列表。
|
||||
//
|
||||
// 该列表用于配置校验,包含以下算法:
|
||||
// - round_robin: 简单轮询,按顺序均匀分配请求
|
||||
// - weighted_round_robin: 加权轮询,按权重比例分配请求
|
||||
// - least_conn: 最少连接,选择活跃连接数最少的目标
|
||||
// - ip_hash: IP 哈希,基于客户端 IP 实现会话保持
|
||||
// - consistent_hash: 一致性哈希,使用虚拟节点实现均匀分布
|
||||
var ValidAlgorithms = []string{
|
||||
// validAlgorithms 定义系统支持的负载均衡算法名称列表。
|
||||
var validAlgorithms = []string{
|
||||
"round_robin",
|
||||
"weighted_round_robin",
|
||||
"least_conn",
|
||||
@ -43,11 +35,11 @@ var ValidAlgorithms = []string{
|
||||
// - alg: 算法名称字符串,如 "round_robin"、"least_conn" 等
|
||||
//
|
||||
// 返回值:
|
||||
// - true: 算法有效(在 ValidAlgorithms 列表中或为空字符串)
|
||||
// - false: 算法无效(不在 ValidAlgorithms 列表中且非空)
|
||||
// - true: 算法有效(在 validAlgorithms 列表中或为空字符串)
|
||||
// - false: 算法无效(不在 validAlgorithms 列表中且非空)
|
||||
func IsValidAlgorithm(alg string) bool {
|
||||
if alg == "" {
|
||||
return true
|
||||
}
|
||||
return slices.Contains(ValidAlgorithms, alg)
|
||||
return slices.Contains(validAlgorithms, alg)
|
||||
}
|
||||
|
||||
@ -869,7 +869,6 @@ func (c *LuaCoroutine) handleCosocketConnect(values []glua.LValue) ([]glua.LValu
|
||||
return []glua.LValue{glua.LNil, glua.LNil}, nil
|
||||
}
|
||||
|
||||
_ = result // 连接成功,返回 1
|
||||
return []glua.LValue{glua.LNumber(1)}, nil
|
||||
}
|
||||
|
||||
|
||||
@ -87,8 +87,8 @@ func DefaultConfig() *Config {
|
||||
EnableLoadLib: false,
|
||||
CoroutineStackSize: 64, // 优化:较小的栈减少内存分配
|
||||
MinimizeStackMemory: true,
|
||||
CoroutinePoolWarmup: 4, // 预热4个协程结构
|
||||
LStatePoolInitialSize: 100, // LState 池预热 100 个
|
||||
CoroutinePoolWarmup: 4, // 预热4个协程结构
|
||||
LStatePoolInitialSize: 100, // LState 池预热 100 个
|
||||
LStatePoolMaxSize: 1000, // LState 池最大 1000 个(与 MaxConcurrentCoroutines 匹配)
|
||||
}
|
||||
}
|
||||
|
||||
@ -151,11 +151,11 @@ func NewEngine(config *Config) (*LuaEngine, error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
engine := &LuaEngine{
|
||||
config: config,
|
||||
codeCache: NewCodeCache(config.CodeCacheSize, config.CodeCacheTTL, config.EnableFileWatch),
|
||||
maxCoroutines: config.MaxConcurrentCoroutines,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
config: config,
|
||||
codeCache: NewCodeCache(config.CodeCacheSize, config.CodeCacheTTL, config.EnableFileWatch),
|
||||
maxCoroutines: config.MaxConcurrentCoroutines,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
sharedDictManager: NewSharedDictManager(),
|
||||
coroutinePool: sync.Pool{
|
||||
New: func() any {
|
||||
|
||||
@ -149,4 +149,4 @@ func (p *LStatePool) Available() int {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
return len(p.pool)
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,4 +93,4 @@ func (h *LuaRouteHandler) ServeHTTP(ctx *fasthttp.RequestCtx) {
|
||||
|
||||
luaCtx.FlushOutput()
|
||||
luaCtx.Release()
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,9 +5,10 @@
|
||||
// 作者:xfy
|
||||
package matcher
|
||||
|
||||
import "maps"
|
||||
|
||||
import "fmt"
|
||||
import (
|
||||
"fmt"
|
||||
"maps"
|
||||
)
|
||||
|
||||
// ConflictDetector 路径冲突检测器。
|
||||
//
|
||||
|
||||
@ -15,9 +15,11 @@
|
||||
// 作者:xfy
|
||||
package middleware
|
||||
|
||||
import "slices"
|
||||
import (
|
||||
"slices"
|
||||
|
||||
import "github.com/valyala/fasthttp"
|
||||
"github.com/valyala/fasthttp"
|
||||
)
|
||||
|
||||
// Middleware 中间件接口,定义中间件的标准方法。
|
||||
//
|
||||
|
||||
@ -111,8 +111,8 @@ func initLuaEngine(luaCfg *config.LuaMiddlewareConfig) (*lua.LuaEngine, error) {
|
||||
EnableOSLib: false, // 安全默认值
|
||||
EnableIOLib: false,
|
||||
EnableLoadLib: false,
|
||||
LStatePoolInitialSize: luaCfg.GlobalSettings.LStatePoolInitialSize,
|
||||
LStatePoolMaxSize: luaCfg.GlobalSettings.LStatePoolMaxSize,
|
||||
LStatePoolInitialSize: luaCfg.GlobalSettings.LStatePoolInitialSize,
|
||||
LStatePoolMaxSize: luaCfg.GlobalSettings.LStatePoolMaxSize,
|
||||
}
|
||||
|
||||
// 设置协程栈优化选项
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user