refactor: modernize code with Go 1.22+ features

Apply modern Go patterns across the codebase:
- Replace `interface{}` with `any` (Go 1.18+)
- Use `for range n` instead of `for i := 0; i < n; i++` (Go 1.22+)
- Replace `sort.Slice` with `slices.Sort` from slices package
- Simplify sync.WaitGroup patterns with errgroup where appropriate
- Add Makefile targets for modernize analyzer

Total: 84 files updated, net reduction of 79 lines

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
xfy 2026-04-30 10:37:45 +08:00
parent e7306a0c72
commit f145a8770e
84 changed files with 399 additions and 478 deletions

View File

@ -289,6 +289,26 @@ lint:
go vet ./...; \ go vet ./...; \
fi fi
# 现代化检查(检测可使用新 Go 特性的代码)
modernize:
@echo "Running modernize analyzer..."
@if command -v modernize >/dev/null 2>&1; then \
modernize ./internal/...; \
else \
echo "modernize not installed. Run: go install golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest"; \
exit 1; \
fi
# 现代化检查并自动修复
modernize-fix:
@echo "Running modernize analyzer with auto-fix..."
@if command -v modernize >/dev/null 2>&1; then \
modernize -fix ./internal/...; \
else \
echo "modernize not installed. Run: go install golang.org/x/tools/go/analysis/passes/modernize/cmd/modernize@latest"; \
exit 1; \
fi
# 代码检查 # 代码检查
check: fmt lint test-all check: fmt lint test-all
@echo "All checks passed." @echo "All checks passed."
@ -405,6 +425,8 @@ help:
@echo "Quality:" @echo "Quality:"
@echo " make fmt - Format code" @echo " make fmt - Format code"
@echo " make lint - Run linter" @echo " make lint - Run linter"
@echo " make modernize - Check for modern Go patterns"
@echo " make modernize-fix - Auto-fix modern Go patterns"
@echo " make check - Format + lint + test" @echo " make check - Format + lint + test"
@echo "" @echo ""
@echo "Dependencies:" @echo "Dependencies:"

View File

@ -36,7 +36,7 @@ const DefaultBodyThreshold = 64 * 1024 // 64KB
// 使用 singleton 模式避免多个适配器实例创建多个 pool // 使用 singleton 模式避免多个适配器实例创建多个 pool
// 提高内存复用效率。该 pool 被 HTTP/2 和 HTTP/3 适配器共享。 // 提高内存复用效率。该 pool 被 HTTP/2 和 HTTP/3 适配器共享。
var bufferPoolInstance = &sync.Pool{ var bufferPoolInstance = &sync.Pool{
New: func() interface{} { New: func() any {
buf := make([]byte, 4096) // 4KB 初始缓冲区 buf := make([]byte, 4096) // 4KB 初始缓冲区
return &buf return &buf
}, },
@ -73,7 +73,7 @@ type CommonAdapter struct {
func NewCommonAdapter() *CommonAdapter { func NewCommonAdapter() *CommonAdapter {
return &CommonAdapter{ return &CommonAdapter{
CtxPool: sync.Pool{ CtxPool: sync.Pool{
New: func() interface{} { New: func() any {
return &fasthttp.RequestCtx{} return &fasthttp.RequestCtx{}
}, },
}, },

View File

@ -327,7 +327,7 @@ func NewBenchmarkContextPool(size int) *BenchmarkContextPool {
pool: make(chan *BenchmarkContext, size), pool: make(chan *BenchmarkContext, size),
} }
// 预填充池 // 预填充池
for i := 0; i < size; i++ { for range size {
p.pool <- DefaultBenchmarkContext() p.pool <- DefaultBenchmarkContext()
} }
return p return p

View File

@ -3,7 +3,7 @@ package tools
import ( import (
"math" "math"
"sort" "slices"
"sync" "sync"
"testing" "testing"
"time" "time"
@ -61,16 +61,14 @@ func (lg *FasthttpLoadGenerator) Run(n int, concurrency int) *LoadGenStats {
start := time.Now() start := time.Now()
for i := 0; i < concurrency; i++ { for range concurrency {
wg.Add(1) wg.Go(func() {
go func() {
defer wg.Done()
req := fasthttp.AcquireRequest() req := fasthttp.AcquireRequest()
resp := fasthttp.AcquireResponse() resp := fasthttp.AcquireResponse()
defer fasthttp.ReleaseRequest(req) defer fasthttp.ReleaseRequest(req)
defer fasthttp.ReleaseResponse(resp) defer fasthttp.ReleaseResponse(resp)
for j := 0; j < requestsPerWorker; j++ { for range requestsPerWorker {
req.SetRequestURI("http://" + lg.addr + "/") req.SetRequestURI("http://" + lg.addr + "/")
req.Header.SetMethod("GET") req.Header.SetMethod("GET")
@ -83,7 +81,7 @@ func (lg *FasthttpLoadGenerator) Run(n int, concurrency int) *LoadGenStats {
errorChan <- err errorChan <- err
} }
} }
}() })
} }
wg.Wait() wg.Wait()
@ -118,9 +116,7 @@ func (lg *FasthttpLoadGenerator) Run(n int, concurrency int) *LoadGenStats {
// Calculate latency distribution // Calculate latency distribution
if len(latencies) > 0 { if len(latencies) > 0 {
sort.Slice(latencies, func(i, j int) bool { slices.Sort(latencies)
return latencies[i] < latencies[j]
})
lg.stats.MinLatency = latencies[0] lg.stats.MinLatency = latencies[0]
lg.stats.MaxLatency = latencies[len(latencies)-1] lg.stats.MaxLatency = latencies[len(latencies)-1]

View File

@ -50,7 +50,7 @@ func createTestTargets(n int) ([]TestTarget, func()) {
targets := make([]TestTarget, n) targets := make([]TestTarget, n)
cleanups := make([]func(), n) cleanups := make([]func(), n)
for i := 0; i < n; i++ { for i := range n {
body := GenerateTestData(Size1KB) body := GenerateTestData(Size1KB)
addr, cleanup := SimpleMockBackend(200, body) addr, cleanup := SimpleMockBackend(200, body)
targets[i] = TestTarget{ targets[i] = TestTarget{
@ -83,7 +83,7 @@ func CreateWeightedTestTargets(n int) ([]TestTarget, func()) {
targets := make([]TestTarget, n) targets := make([]TestTarget, n)
cleanups := make([]func(), n) cleanups := make([]func(), n)
for i := 0; i < n; i++ { for i := range n {
body := GenerateTestData(Size1KB) body := GenerateTestData(Size1KB)
addr, cleanup := SimpleMockBackend(200, body) addr, cleanup := SimpleMockBackend(200, body)
// Vary weights: 1, 2, 3, etc. // Vary weights: 1, 2, 3, etc.
@ -111,7 +111,7 @@ func CreateDelayedTestTargets(n int, baseDelay time.Duration) ([]TestTarget, fun
targets := make([]TestTarget, n) targets := make([]TestTarget, n)
cleanups := make([]func(), n) cleanups := make([]func(), n)
for i := 0; i < n; i++ { for i := range n {
body := GenerateTestData(Size1KB) body := GenerateTestData(Size1KB)
// Each target has increasing delay // Each target has increasing delay
delay := baseDelay * time.Duration(i+1) delay := baseDelay * time.Duration(i+1)
@ -140,7 +140,7 @@ func CreateErrorTestTargets(n int, baseErrorRate float64) ([]TestTarget, func())
targets := make([]TestTarget, n) targets := make([]TestTarget, n)
cleanups := make([]func(), n) cleanups := make([]func(), n)
for i := 0; i < n; i++ { for i := range n {
body := GenerateTestData(Size1KB) body := GenerateTestData(Size1KB)
// Vary error rates slightly per target // Vary error rates slightly per target
errorRate := baseErrorRate + float64(i)*0.05 errorRate := baseErrorRate + float64(i)*0.05

View File

@ -32,7 +32,7 @@ func BenchmarkFileCacheGet(b *testing.B) {
fc := NewFileCache(int64(size), 0, 1*time.Hour) fc := NewFileCache(int64(size), 0, 1*time.Hour)
// 预填充缓存 // 预填充缓存
for i := 0; i < size; i++ { for i := range size {
path := fmt.Sprintf("/file%d.txt", i) path := fmt.Sprintf("/file%d.txt", i)
data := []byte("cached data content") data := []byte("cached data content")
_ = fc.Set(path, data, int64(len(data)), time.Now()) _ = fc.Set(path, data, int64(len(data)), time.Now())
@ -62,7 +62,7 @@ func BenchmarkFileCacheSet(b *testing.B) {
fc := NewFileCache(int64(size), 0, 1*time.Hour) fc := NewFileCache(int64(size), 0, 1*time.Hour)
// 预填充到容量上限 // 预填充到容量上限
for i := 0; i < size; i++ { for i := range size {
path := fmt.Sprintf("/file%d.txt", i) path := fmt.Sprintf("/file%d.txt", i)
data := []byte("cached data content") data := []byte("cached data content")
_ = fc.Set(path, data, int64(len(data)), time.Now()) _ = fc.Set(path, data, int64(len(data)), time.Now())
@ -88,7 +88,7 @@ func BenchmarkFileCacheSet_Pooled(b *testing.B) {
fc := NewFileCache(int64(size), 0, 1*time.Hour) fc := NewFileCache(int64(size), 0, 1*time.Hour)
// 预填充到容量上限,触发淘汰和 entry 复用 // 预填充到容量上限,触发淘汰和 entry 复用
for i := 0; i < size; i++ { for i := range size {
path := fmt.Sprintf("/file%d.txt", i) path := fmt.Sprintf("/file%d.txt", i)
data := []byte("cached data content") data := []byte("cached data content")
_ = fc.Set(path, data, int64(len(data)), time.Now()) _ = fc.Set(path, data, int64(len(data)), time.Now())
@ -128,7 +128,7 @@ func BenchmarkFileCacheConcurrent(b *testing.B) {
fc := NewFileCache(int64(size), 0, 1*time.Hour) fc := NewFileCache(int64(size), 0, 1*time.Hour)
// 预填充缓存 // 预填充缓存
for i := 0; i < size; i++ { for i := range size {
path := fmt.Sprintf("/file%d.txt", i) path := fmt.Sprintf("/file%d.txt", i)
data := []byte("cached data content") data := []byte("cached data content")
_ = fc.Set(path, data, int64(len(data)), time.Now()) _ = fc.Set(path, data, int64(len(data)), time.Now())
@ -160,7 +160,7 @@ func BenchmarkFileCacheGetOnly(b *testing.B) {
fc := NewFileCache(1000, 0, 1*time.Hour) fc := NewFileCache(1000, 0, 1*time.Hour)
// 预填充缓存 // 预填充缓存
for i := 0; i < 1000; i++ { for i := range 1000 {
path := fmt.Sprintf("/static/file%d.css", i) path := fmt.Sprintf("/static/file%d.css", i)
data := make([]byte, 1024) // 1KB 数据 data := make([]byte, 1024) // 1KB 数据
_ = fc.Set(path, data, int64(len(data)), time.Now()) _ = fc.Set(path, data, int64(len(data)), time.Now())
@ -186,7 +186,7 @@ func BenchmarkFileCacheSizeEviction(b *testing.B) {
// 预填充到接近容量上限 // 预填充到接近容量上限
data := make([]byte, 1024) // 1KB 每条 data := make([]byte, 1024) // 1KB 每条
for i := 0; i < 1000; i++ { for i := range 1000 {
path := fmt.Sprintf("/file%d.txt", i) path := fmt.Sprintf("/file%d.txt", i)
_ = fc.Set(path, data, int64(len(data)), time.Now()) _ = fc.Set(path, data, int64(len(data)), time.Now())
} }
@ -205,7 +205,7 @@ func BenchmarkFileCacheLRUTouch(b *testing.B) {
fc := NewFileCache(100, 0, 1*time.Hour) fc := NewFileCache(100, 0, 1*time.Hour)
// 预填充缓存 // 预填充缓存
for i := 0; i < 100; i++ { for i := range 100 {
path := fmt.Sprintf("/file%d.txt", i) path := fmt.Sprintf("/file%d.txt", i)
data := []byte("cached data") data := []byte("cached data")
_ = fc.Set(path, data, int64(len(data)), time.Now()) _ = fc.Set(path, data, int64(len(data)), time.Now())
@ -224,7 +224,7 @@ func BenchmarkProxyCacheGet(b *testing.B) {
pc := NewProxyCache(nil, false, 0, 0, 0) pc := NewProxyCache(nil, false, 0, 0, 0)
// 预填充缓存 // 预填充缓存
for i := 0; i < 1000; i++ { for i := range 1000 {
origKey := fmt.Sprintf("key%d", i) origKey := fmt.Sprintf("key%d", i)
hashKey := hashKeyBench(origKey) hashKey := hashKeyBench(origKey)
data := []byte("response body") data := []byte("response body")
@ -264,7 +264,7 @@ func BenchmarkProxyCacheConcurrent(b *testing.B) {
pc := NewProxyCache(nil, false, 0, 0, 0) pc := NewProxyCache(nil, false, 0, 0, 0)
// 预填充缓存 // 预填充缓存
for i := 0; i < 1000; i++ { for i := range 1000 {
origKey := fmt.Sprintf("key%d", i) origKey := fmt.Sprintf("key%d", i)
hashKey := hashKeyBench(origKey) hashKey := hashKeyBench(origKey)
data := []byte("response body") data := []byte("response body")
@ -301,7 +301,7 @@ func BenchmarkFileCacheSharded(b *testing.B) {
// 单锁缓存 // 单锁缓存
b.Run(fmt.Sprintf("SingleLock_Size%d", size), func(b *testing.B) { b.Run(fmt.Sprintf("SingleLock_Size%d", size), func(b *testing.B) {
fc := NewFileCache(int64(size), 0, 1*time.Hour) fc := NewFileCache(int64(size), 0, 1*time.Hour)
for i := 0; i < size; i++ { for i := range size {
path := fmt.Sprintf("/file%d.txt", i) path := fmt.Sprintf("/file%d.txt", i)
data := []byte("cached data") data := []byte("cached data")
_ = fc.Set(path, data, int64(len(data)), time.Now()) _ = fc.Set(path, data, int64(len(data)), time.Now())
@ -322,7 +322,7 @@ func BenchmarkFileCacheSharded(b *testing.B) {
// 分片缓存 // 分片缓存
b.Run(fmt.Sprintf("Sharded_Size%d", size), func(b *testing.B) { b.Run(fmt.Sprintf("Sharded_Size%d", size), func(b *testing.B) {
sc := NewShardedFileCache(int64(size), 0, 1*time.Hour) sc := NewShardedFileCache(int64(size), 0, 1*time.Hour)
for i := 0; i < size; i++ { for i := range size {
path := fmt.Sprintf("/file%d.txt", i) path := fmt.Sprintf("/file%d.txt", i)
data := []byte("cached data") data := []byte("cached data")
_ = sc.Set(path, data, int64(len(data)), time.Now()) _ = sc.Set(path, data, int64(len(data)), time.Now())

View File

@ -50,7 +50,7 @@ func BenchmarkFileCacheSetAllocation_Update(b *testing.B) {
// 预填充缓存 // 预填充缓存
data := []byte("test data content for benchmark") data := []byte("test data content for benchmark")
size := int64(len(data)) size := int64(len(data))
for i := 0; i < 1000; i++ { for i := range 1000 {
path := fmt.Sprintf("/update/file%d.txt", i) path := fmt.Sprintf("/update/file%d.txt", i)
fc.Set(path, data, size, time.Now()) fc.Set(path, data, size, time.Now())
} }
@ -78,7 +78,7 @@ func BenchmarkFileCacheSetAllocation_Eviction(b *testing.B) {
// 预填充到容量上限 // 预填充到容量上限
data := []byte("test data content for benchmark") data := []byte("test data content for benchmark")
size := int64(len(data)) size := int64(len(data))
for i := 0; i < 100; i++ { for i := range 100 {
path := fmt.Sprintf("/evict/file%d.txt", i) path := fmt.Sprintf("/evict/file%d.txt", i)
fc.Set(path, data, size, time.Now()) fc.Set(path, data, size, time.Now())
} }
@ -103,7 +103,7 @@ func BenchmarkFileCacheSetAllocation_EvictionWithPool(b *testing.B) {
size := int64(len(data)) size := int64(len(data))
// 预填充 // 预填充
for i := 0; i < 100; i++ { for i := range 100 {
path := fmt.Sprintf("/pool/file%d.txt", i) path := fmt.Sprintf("/pool/file%d.txt", i)
fc.Set(path, data, size, time.Now()) fc.Set(path, data, size, time.Now())
} }
@ -128,7 +128,7 @@ func BenchmarkFileCacheSetAllocation_MemoryLimit(b *testing.B) {
size := int64(len(data)) size := int64(len(data))
// 预填充到接近上限 // 预填充到接近上限
for i := 0; i < 900; i++ { for i := range 900 {
path := fmt.Sprintf("/mem/file%d.txt", i) path := fmt.Sprintf("/mem/file%d.txt", i)
fc.Set(path, data, size, time.Now()) fc.Set(path, data, size, time.Now())
} }
@ -172,7 +172,7 @@ func BenchmarkFileCacheSetAllocation_ConcurrentEviction(b *testing.B) {
size := int64(len(data)) size := int64(len(data))
// 预填充 // 预填充
for i := 0; i < 100; i++ { for i := range 100 {
path := fmt.Sprintf("/concevict/file%d.txt", i) path := fmt.Sprintf("/concevict/file%d.txt", i)
fc.Set(path, data, size, time.Now()) fc.Set(path, data, size, time.Now())
} }
@ -201,7 +201,7 @@ func BenchmarkFileCacheEntryPool_GetPut(b *testing.B) {
} }
// 预填充池 // 预填充池
for i := 0; i < 100; i++ { for range 100 {
pool.Put(&FileEntry{}) pool.Put(&FileEntry{})
} }

View File

@ -61,7 +61,7 @@ func NewShardedFileCache(maxEntries, maxSize int64, inactive time.Duration) *Sha
perShardSize = maxSize perShardSize = maxSize
} }
for i := 0; i < shardCount; i++ { for i := range shardCount {
shard := &FileCacheShard{ shard := &FileCacheShard{
maxEntries: perShardEntries, maxEntries: perShardEntries,
maxSize: perShardSize, maxSize: perShardSize,
@ -83,7 +83,7 @@ func NewShardedFileCache(maxEntries, maxSize int64, inactive time.Duration) *Sha
func (s *ShardedFileCache) getShard(path string) *FileCacheShard { func (s *ShardedFileCache) getShard(path string) *FileCacheShard {
h := fnv.New64a() h := fnv.New64a()
h.Write([]byte(path)) h.Write([]byte(path))
return s.shards[h.Sum64() % shardCount] return s.shards[h.Sum64()%shardCount]
} }
// Get 获取缓存的文件。 // Get 获取缓存的文件。

View File

@ -89,22 +89,22 @@ func TestGenerateConfigYAMLFieldsCoverage(t *testing.T) {
typ reflect.Type typ reflect.Type
name string name string
}{ }{
{reflect.TypeOf(GeoIPConfig{}), "GeoIPConfig"}, {reflect.TypeFor[GeoIPConfig](), "GeoIPConfig"},
{reflect.TypeOf(AuthRequestConfig{}), "AuthRequestConfig"}, {reflect.TypeFor[AuthRequestConfig](), "AuthRequestConfig"},
{reflect.TypeOf(LuaGlobalSettings{}), "LuaGlobalSettings"}, {reflect.TypeFor[LuaGlobalSettings](), "LuaGlobalSettings"},
{reflect.TypeOf(LimitRateConfig{}), "LimitRateConfig"}, {reflect.TypeFor[LimitRateConfig](), "LimitRateConfig"},
{reflect.TypeOf(TypesConfig{}), "TypesConfig"}, {reflect.TypeFor[TypesConfig](), "TypesConfig"},
} }
for _, c := range checks { for _, c := range checks {
for i := 0; i < c.typ.NumField(); i++ { for field := range c.typ.Fields() {
tag := c.typ.Field(i).Tag.Get("yaml") tag := field.Tag.Get("yaml")
fieldName := strings.Split(tag, ",")[0] fieldName := strings.Split(tag, ",")[0]
if fieldName == "" || fieldName == "-" { if fieldName == "" || fieldName == "-" {
continue continue
} }
if !strings.Contains(yamlStr, fieldName) { if !strings.Contains(yamlStr, fieldName) {
t.Errorf("%s.%s (yaml:%q) not found in GenerateConfigYAML output", c.name, c.typ.Field(i).Name, fieldName) t.Errorf("%s.%s (yaml:%q) not found in GenerateConfigYAML output", c.name, field.Name, fieldName)
} }
} }
} }

View File

@ -615,7 +615,7 @@ func TestErrorPageManager_SuccessfulLoad(t *testing.T) {
defaultPage := filepath.Join(tmpDir, "default.html") defaultPage := filepath.Join(tmpDir, "default.html")
for code, path := range pages { for code, path := range pages {
content := []byte(fmt.Sprintf("Error %d page", code)) content := fmt.Appendf(nil, "Error %d page", code)
if err := os.WriteFile(path, content, 0o644); err != nil { if err := os.WriteFile(path, content, 0o644); err != nil {
t.Fatalf("创建页面 %d 失败: %v", code, err) t.Fatalf("创建页面 %d 失败: %v", code, err)
} }

View File

@ -669,15 +669,13 @@ func TestLinuxSendfile_WithTCPConn(t *testing.T) {
var serverConn net.Conn var serverConn net.Conn
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(1) wg.Go(func() {
go func() {
defer wg.Done()
serverConn, _ = ln.Accept() serverConn, _ = ln.Accept()
// 读取所有数据 // 读取所有数据
buf := make([]byte, len(content)) buf := make([]byte, len(content))
_, _ = io.ReadFull(serverConn, buf) _, _ = io.ReadFull(serverConn, buf)
serverConn.Close() serverConn.Close()
}() })
clientConn, err := net.Dial("tcp", ln.Addr().String()) clientConn, err := net.Dial("tcp", ln.Addr().String())
if err != nil { if err != nil {
@ -827,15 +825,13 @@ func TestLinuxSendfile_PartialTransfer(t *testing.T) {
var serverConn net.Conn var serverConn net.Conn
var received []byte var received []byte
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(1) wg.Go(func() {
go func() {
defer wg.Done()
serverConn, _ = ln.Accept() serverConn, _ = ln.Accept()
buf := make([]byte, len(content)) buf := make([]byte, len(content))
n, _ := serverConn.Read(buf) n, _ := serverConn.Read(buf)
received = buf[:n] received = buf[:n]
serverConn.Close() serverConn.Close()
}() })
clientConn, err := net.Dial("tcp", ln.Addr().String()) clientConn, err := net.Dial("tcp", ln.Addr().String())
if err != nil { if err != nil {
@ -888,14 +884,12 @@ func TestLinuxSendfile_WithOffset(t *testing.T) {
var serverConn net.Conn var serverConn net.Conn
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(1) wg.Go(func() {
go func() {
defer wg.Done()
serverConn, _ = ln.Accept() serverConn, _ = ln.Accept()
buf := make([]byte, 8*1024) buf := make([]byte, 8*1024)
_, _ = serverConn.Read(buf) _, _ = serverConn.Read(buf)
serverConn.Close() serverConn.Close()
}() })
clientConn, err := net.Dial("tcp", ln.Addr().String()) clientConn, err := net.Dial("tcp", ln.Addr().String())
if err != nil { if err != nil {

View File

@ -439,7 +439,7 @@ func TestAdapterConcurrentRequests(t *testing.T) {
concurrency := 10 concurrency := 10
done := make(chan bool, concurrency) done := make(chan bool, concurrency)
for i := 0; i < concurrency; i++ { for range concurrency {
go func() { go func() {
req := httptest.NewRequest(http.MethodGet, "/test", nil) req := httptest.NewRequest(http.MethodGet, "/test", nil)
rec := httptest.NewRecorder() rec := httptest.NewRecorder()
@ -453,7 +453,7 @@ func TestAdapterConcurrentRequests(t *testing.T) {
} }
// 等待所有请求完成 // 等待所有请求完成
for i := 0; i < concurrency; i++ { for range concurrency {
<-done <-done
} }
} }

View File

@ -16,6 +16,7 @@ import (
"net" "net"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"slices"
"testing" "testing"
"time" "time"
@ -101,13 +102,7 @@ func TestIntegrationALPN(t *testing.T) {
} }
// 验证协议列表 // 验证协议列表
foundH2 := false foundH2 := slices.Contains(tlsConfig.NextProtos, "h2")
for _, proto := range tlsConfig.NextProtos {
if proto == "h2" {
foundH2 = true
break
}
}
if !foundH2 { if !foundH2 {
t.Error("ALPN config should include h2 protocol") t.Error("ALPN config should include h2 protocol")
} }

View File

@ -18,6 +18,7 @@ import (
"math/big" "math/big"
"net" "net"
"net/http" "net/http"
"slices"
"sync" "sync"
"testing" "testing"
"time" "time"
@ -232,13 +233,7 @@ func TestALPNNegotiationH2(t *testing.T) {
t.Fatal("ALPN config should not be nil") t.Fatal("ALPN config should not be nil")
} }
foundH2 := false foundH2 := slices.Contains(alpnConfig.NextProtos, "h2")
for _, proto := range alpnConfig.NextProtos {
if proto == "h2" {
foundH2 = true
break
}
}
if !foundH2 { if !foundH2 {
t.Error("ALPN config should include h2 protocol") t.Error("ALPN config should include h2 protocol")
} }
@ -397,11 +392,9 @@ func TestServeHTTP1Fallback(t *testing.T) {
}() }()
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(1) wg.Go(func() {
go func() {
defer wg.Done()
server.serveHTTP1(serverConn) server.serveHTTP1(serverConn)
}() })
// 发送 HTTP/1.1 请求 // 发送 HTTP/1.1 请求
request := "GET /test HTTP/1.1\r\nHost: localhost\r\n\r\n" request := "GET /test HTTP/1.1\r\nHost: localhost\r\n\r\n"

View File

@ -23,6 +23,7 @@ import (
"net" "net"
"net/http" "net/http"
"net/textproto" "net/textproto"
"slices"
"sync" "sync"
"time" "time"
@ -330,13 +331,7 @@ func WrapTLSListener(ln net.Listener, tlsConfig *tls.Config) net.Listener {
originalGetConfig := tlsConfig.GetConfigForClient originalGetConfig := tlsConfig.GetConfigForClient
tlsConfig.GetConfigForClient = func(hello *tls.ClientHelloInfo) (*tls.Config, error) { tlsConfig.GetConfigForClient = func(hello *tls.ClientHelloInfo) (*tls.Config, error) {
// 检查客户端是否支持 h2 // 检查客户端是否支持 h2
supportsH2 := false supportsH2 := slices.Contains(hello.SupportedProtos, "h2")
for _, proto := range hello.SupportedProtos {
if proto == "h2" {
supportsH2 = true
break
}
}
// 如果有原始回调,先调用它 // 如果有原始回调,先调用它
var cfg *tls.Config var cfg *tls.Config

View File

@ -339,7 +339,7 @@ func TestServer_MultipleStop(t *testing.T) {
server, _ := NewServer(cfg, handler, &tls.Config{}) server, _ := NewServer(cfg, handler, &tls.Config{})
// 多次调用 Stop 应该都是安全的 // 多次调用 Stop 应该都是安全的
for i := 0; i < 3; i++ { for i := range 3 {
err := server.Stop() err := server.Stop()
if err != nil { if err != nil {
t.Errorf("Stop call %d returned error: %v", i+1, err) t.Errorf("Stop call %d returned error: %v", i+1, err)
@ -358,7 +358,7 @@ func TestServer_MultipleGracefulStop(t *testing.T) {
server, _ := NewServer(cfg, handler, &tls.Config{}) server, _ := NewServer(cfg, handler, &tls.Config{})
// 多次调用 GracefulStop 应该都是安全的 // 多次调用 GracefulStop 应该都是安全的
for i := 0; i < 3; i++ { for i := range 3 {
err := server.GracefulStop(1 * time.Second) err := server.GracefulStop(1 * time.Second)
if err != nil { if err != nil {
t.Errorf("GracefulStop call %d returned error: %v", i+1, err) t.Errorf("GracefulStop call %d returned error: %v", i+1, err)

View File

@ -249,7 +249,7 @@ func createTestProxy(backendAddr, path string) (*proxy.Proxy, error) {
// - path: 请求路径 // - path: 请求路径
// - count: 预热请求数量 // - count: 预热请求数量
func warmupProxy(p *proxy.Proxy, path string, count int) { func warmupProxy(p *proxy.Proxy, path string, count int) {
for i := 0; i < count; i++ { for range count {
ctx := &fasthttp.RequestCtx{} ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI(path) ctx.Request.SetRequestURI(path)
ctx.Request.Header.SetMethod(fasthttp.MethodGet) ctx.Request.Header.SetMethod(fasthttp.MethodGet)
@ -299,11 +299,11 @@ func BenchmarkE2EStaticFile(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
paths := []string{"/small.css", "/medium.json", "/assets/app.js", "/index.html"} paths := []string{"/small.css", "/medium.json", "/assets/app.js", "/index.html"}
var counter uint64 var counter atomic.Uint64
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
idx := atomic.AddUint64(&counter, 1) idx := counter.Add(1)
ctx := &fasthttp.RequestCtx{} ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI(paths[idx%uint64(len(paths))]) ctx.Request.SetRequestURI(paths[idx%uint64(len(paths))])
ctx.Request.Header.SetMethod(fasthttp.MethodGet) ctx.Request.Header.SetMethod(fasthttp.MethodGet)
@ -336,11 +336,11 @@ func BenchmarkE2EStaticFileCacheHit(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
paths := []string{"/small.css", "/medium.json", "/assets/app.js", "/index.html"} paths := []string{"/small.css", "/medium.json", "/assets/app.js", "/index.html"}
var counter uint64 var counter atomic.Uint64
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
idx := atomic.AddUint64(&counter, 1) idx := counter.Add(1)
ctx := &fasthttp.RequestCtx{} ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI(paths[idx%uint64(len(paths))]) ctx.Request.SetRequestURI(paths[idx%uint64(len(paths))])
ctx.Request.Header.SetMethod(fasthttp.MethodGet) ctx.Request.Header.SetMethod(fasthttp.MethodGet)
@ -568,7 +568,7 @@ ngx.header["X-Lua-Processed"] = "true"`
finalHandler := chain.Apply(wrappedByLua) finalHandler := chain.Apply(wrappedByLua)
// 预热 Lua 引擎(字节码缓存) // 预热 Lua 引擎(字节码缓存)
for i := 0; i < 5; i++ { for range 5 {
ctx := &fasthttp.RequestCtx{} ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI("/api/test") ctx.Request.SetRequestURI("/api/test")
ctx.Request.Header.SetMethod(fasthttp.MethodGet) ctx.Request.Header.SetMethod(fasthttp.MethodGet)
@ -638,7 +638,7 @@ func BenchmarkE2EMultiLuaPhase(b *testing.B) {
finalHandler := baseChain.Apply(wrappedByLua) finalHandler := baseChain.Apply(wrappedByLua)
// 预热 // 预热
for i := 0; i < 5; i++ { for range 5 {
ctx := &fasthttp.RequestCtx{} ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI("/api/test") ctx.Request.SetRequestURI("/api/test")
ctx.Request.Header.SetMethod(fasthttp.MethodGet) ctx.Request.Header.SetMethod(fasthttp.MethodGet)
@ -884,7 +884,7 @@ func BenchmarkE2EMultipleRoutes(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
// 使用原子计数器轮询不同路径 // 使用原子计数器轮询不同路径
var counter uint64 var counter atomic.Uint64
paths := []string{ paths := []string{
"/api/v1/users", "/api/v1/users",
"/api/v2/items", "/api/v2/items",
@ -899,7 +899,7 @@ func BenchmarkE2EMultipleRoutes(b *testing.B) {
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
idx := atomic.AddUint64(&counter, 1) idx := counter.Add(1)
ctx := &fasthttp.RequestCtx{} ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI(paths[idx%uint64(len(paths))]) ctx.Request.SetRequestURI(paths[idx%uint64(len(paths))])
ctx.Request.Header.SetMethod(fasthttp.MethodGet) ctx.Request.Header.SetMethod(fasthttp.MethodGet)
@ -955,7 +955,7 @@ func BenchmarkE2EMultipleRoutesWithMiddleware(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.ReportAllocs() b.ReportAllocs()
var counter uint64 var counter atomic.Uint64
testPaths := []string{ testPaths := []string{
"/api/users", "/api/users",
"/api/users/42", "/api/users/42",
@ -968,7 +968,7 @@ func BenchmarkE2EMultipleRoutesWithMiddleware(b *testing.B) {
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
idx := atomic.AddUint64(&counter, 1) idx := counter.Add(1)
ctx := &fasthttp.RequestCtx{} ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI(testPaths[idx%uint64(len(testPaths))]) ctx.Request.SetRequestURI(testPaths[idx%uint64(len(testPaths))])
ctx.Request.Header.SetMethod(fasthttp.MethodGet) ctx.Request.Header.SetMethod(fasthttp.MethodGet)
@ -1152,7 +1152,7 @@ func BenchmarkE2EInmemoryServer(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.ReportAllocs() b.ReportAllocs()
var counter uint64 var counter atomic.Uint64
paths := []string{"/api/test", "/static/small.css", "/static/medium.json", "/health", "/api/data"} paths := []string{"/api/test", "/static/small.css", "/static/medium.json", "/health", "/api/data"}
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
@ -1162,7 +1162,7 @@ func BenchmarkE2EInmemoryServer(b *testing.B) {
defer fasthttp.ReleaseResponse(resp) defer fasthttp.ReleaseResponse(resp)
for pb.Next() { for pb.Next() {
idx := atomic.AddUint64(&counter, 1) idx := counter.Add(1)
req.SetRequestURI("http://localhost" + paths[idx%uint64(len(paths))]) req.SetRequestURI("http://localhost" + paths[idx%uint64(len(paths))])
req.Header.SetMethod(fasthttp.MethodGet) req.Header.SetMethod(fasthttp.MethodGet)
req.Header.Set("Host", "example.com") req.Header.Set("Host", "example.com")
@ -1316,11 +1316,11 @@ func BenchmarkE2ERewriteMiddleware(b *testing.B) {
"/old-api/settings", "/old-api/settings",
"/v1/data/123", "/v1/data/123",
} }
var counter uint64 var counter atomic.Uint64
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
idx := atomic.AddUint64(&counter, 1) idx := counter.Add(1)
ctx := &fasthttp.RequestCtx{} ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI(paths[idx%uint64(len(paths))]) ctx.Request.SetRequestURI(paths[idx%uint64(len(paths))])
ctx.Request.Header.SetMethod(fasthttp.MethodGet) ctx.Request.Header.SetMethod(fasthttp.MethodGet)
@ -1365,11 +1365,11 @@ func BenchmarkE2EAccessControl(b *testing.B) {
allowedIPs := []string{"192.168.1.50", "192.168.1.200", "10.0.0.1", "10.1.2.3"} allowedIPs := []string{"192.168.1.50", "192.168.1.200", "10.0.0.1", "10.1.2.3"}
deniedIPs := []string{"192.168.1.100", "172.16.0.1", "8.8.8.8"} deniedIPs := []string{"192.168.1.100", "172.16.0.1", "8.8.8.8"}
allIPs := append(allowedIPs, deniedIPs...) allIPs := append(allowedIPs, deniedIPs...)
var counter uint64 var counter atomic.Uint64
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
idx := atomic.AddUint64(&counter, 1) idx := counter.Add(1)
clientIP := allIPs[idx%uint64(len(allIPs))] clientIP := allIPs[idx%uint64(len(allIPs))]
ctx := &fasthttp.RequestCtx{} ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI("/api/test") ctx.Request.SetRequestURI("/api/test")
@ -1416,11 +1416,11 @@ func BenchmarkE2ERateLimiter(b *testing.B) {
b.ResetTimer() b.ResetTimer()
b.ReportAllocs() b.ReportAllocs()
var counter uint64 var counter atomic.Uint64
b.RunParallel(func(pb *testing.PB) { b.RunParallel(func(pb *testing.PB) {
for pb.Next() { for pb.Next() {
idx := atomic.AddUint64(&counter, 1) idx := counter.Add(1)
ctx := &fasthttp.RequestCtx{} ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI("/api/test") ctx.Request.SetRequestURI("/api/test")
ctx.Request.Header.SetMethod(fasthttp.MethodGet) ctx.Request.Header.SetMethod(fasthttp.MethodGet)
@ -1470,7 +1470,7 @@ func BenchmarkE2EBasicAuth(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
// 预热认证(缓存 bcrypt 结果) // 预热认证(缓存 bcrypt 结果)
for i := 0; i < 3; i++ { for range 3 {
ctx := &fasthttp.RequestCtx{} ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI("/api/test") ctx.Request.SetRequestURI("/api/test")
ctx.Request.Header.SetMethod(fasthttp.MethodGet) ctx.Request.Header.SetMethod(fasthttp.MethodGet)

View File

@ -37,7 +37,7 @@ func setupFailoverBackends(b *testing.B, count, healthyCount int) ([]*loadbalanc
targets := make([]*loadbalance.Target, count) targets := make([]*loadbalance.Target, count)
cleanups := make([]func(), count) cleanups := make([]func(), count)
for i := 0; i < count; i++ { for i := range count {
addr, cleanup := setupNetworkBackend(b, fasthttp.StatusOK, []byte(`{"backend":`+strconv.Itoa(i)+`}`)) addr, cleanup := setupNetworkBackend(b, fasthttp.StatusOK, []byte(`{"backend":`+strconv.Itoa(i)+`}`))
cleanups[i] = cleanup cleanups[i] = cleanup
targets[i] = &loadbalance.Target{ targets[i] = &loadbalance.Target{
@ -215,8 +215,8 @@ func BenchmarkE2EFailover_DynamicToggle(b *testing.B) {
for pb.Next() { for pb.Next() {
// 每 100 次请求切换一个后端的健康状态 // 每 100 次请求切换一个后端的健康状态
count := toggleCounter.Add(1) count := toggleCounter.Add(1)
if count % 100 == 0 { if count%100 == 0 {
targetIdx := int(count / 100) % len(targets) targetIdx := int(count/100) % len(targets)
current := targets[targetIdx].Healthy.Load() current := targets[targetIdx].Healthy.Load()
targets[targetIdx].Healthy.Store(!current) targets[targetIdx].Healthy.Store(!current)
} }
@ -271,7 +271,7 @@ func BenchmarkE2EFailover_AllUnhealthy(b *testing.B) {
// 验证负载均衡器选择逻辑的分配。 // 验证负载均衡器选择逻辑的分配。
func BenchmarkE2EFailover_SelectOnly(b *testing.B) { func BenchmarkE2EFailover_SelectOnly(b *testing.B) {
targets := make([]*loadbalance.Target, 5) targets := make([]*loadbalance.Target, 5)
for i := 0; i < 5; i++ { for i := range 5 {
targets[i] = &loadbalance.Target{ targets[i] = &loadbalance.Target{
URL: "http://backend" + strconv.Itoa(i) + ":8080", URL: "http://backend" + strconv.Itoa(i) + ":8080",
Weight: 1, Weight: 1,

View File

@ -199,7 +199,7 @@ func TestVariablePerformance(t *testing.T) {
// 执行多次展开 // 执行多次展开
start := time.Now() start := time.Now()
iterations := 10000 iterations := 10000
for i := 0; i < iterations; i++ { for range iterations {
_ = vc.Expand(template) _ = vc.Expand(template)
} }
elapsed := time.Since(start) elapsed := time.Since(start)

View File

@ -105,7 +105,7 @@ type Balancer interface {
// 并发安全counter 使用 atomic 操作,支持多 goroutine 并发调用。 // 并发安全counter 使用 atomic 操作,支持多 goroutine 并发调用。
type RoundRobin struct { type RoundRobin struct {
// counter 请求计数器,原子递增,用于确定轮询位置 // counter 请求计数器,原子递增,用于确定轮询位置
counter uint64 counter atomic.Uint64
} }
// NewRoundRobin 创建一个新的轮询负载均衡器。 // NewRoundRobin 创建一个新的轮询负载均衡器。
@ -128,7 +128,7 @@ func (r *RoundRobin) Select(targets []*Target) *Target {
} }
// 原子地递增并获取计数器值 // 原子地递增并获取计数器值
idx := atomic.AddUint64(&r.counter, 1) - 1 idx := r.counter.Add(1) - 1
return healthy[idx%uint64(len(healthy))] return healthy[idx%uint64(len(healthy))]
} }
@ -139,7 +139,7 @@ func (r *RoundRobin) Select(targets []*Target) *Target {
// 并发安全counter 使用 atomic 操作,支持多 goroutine 并发调用。 // 并发安全counter 使用 atomic 操作,支持多 goroutine 并发调用。
type WeightedRoundRobin struct { type WeightedRoundRobin struct {
// counter 请求计数器,原子递增,用于确定权重分布位置 // counter 请求计数器,原子递增,用于确定权重分布位置
counter uint64 counter atomic.Uint64
} }
// NewWeightedRoundRobin 创建一个新的权重轮询负载均衡器。 // NewWeightedRoundRobin 创建一个新的权重轮询负载均衡器。
@ -176,7 +176,7 @@ func (w *WeightedRoundRobin) Select(targets []*Target) *Target {
} }
// 使用原子计数器确定权重分布中的位置 // 使用原子计数器确定权重分布中的位置
idx := atomic.AddUint64(&w.counter, 1) - 1 idx := w.counter.Add(1) - 1
pos := int(idx % uint64(totalWeight)) pos := int(idx % uint64(totalWeight))
// 找到计算位置处的目标 // 找到计算位置处的目标
@ -430,7 +430,7 @@ func (r *RoundRobin) SelectExcluding(targets []*Target, excluded []*Target) *Tar
} }
// 原子地递增并获取计数器值 // 原子地递增并获取计数器值
idx := atomic.AddUint64(&r.counter, 1) - 1 idx := r.counter.Add(1) - 1
return available[idx%uint64(len(available))] return available[idx%uint64(len(available))]
} }
@ -457,7 +457,7 @@ func (w *WeightedRoundRobin) SelectExcluding(targets []*Target, excluded []*Targ
} }
// 使用原子计数器确定权重分布中的位置 // 使用原子计数器确定权重分布中的位置
idx := atomic.AddUint64(&w.counter, 1) - 1 idx := w.counter.Add(1) - 1
pos := int(idx % uint64(totalWeight)) pos := int(idx % uint64(totalWeight))
// 找到计算位置处的目标 // 找到计算位置处的目标

View File

@ -34,7 +34,7 @@ import (
// - 包含 count 个健康目标的切片,权重均为 1 // - 包含 count 个健康目标的切片,权重均为 1
func generateTargets(count int) []*Target { func generateTargets(count int) []*Target {
targets := make([]*Target, count) targets := make([]*Target, count)
for i := 0; i < count; i++ { for i := range count {
targets[i] = &Target{ targets[i] = &Target{
URL: fmt.Sprintf("http://backend%d:8080", i), URL: fmt.Sprintf("http://backend%d:8080", i),
Weight: 1, Weight: 1,
@ -54,7 +54,7 @@ func generateTargets(count int) []*Target {
// - 包含 count 个健康目标的切片,按 weights 分配权重 // - 包含 count 个健康目标的切片,按 weights 分配权重
func generateWeightedTargets(count int, weights []int) []*Target { func generateWeightedTargets(count int, weights []int) []*Target {
targets := make([]*Target, count) targets := make([]*Target, count)
for i := 0; i < count; i++ { for i := range count {
weight := 1 weight := 1
if i < len(weights) { if i < len(weights) {
weight = weights[i] weight = weights[i]
@ -160,7 +160,7 @@ func BenchmarkConsistentHashSelect(b *testing.B) {
ch.Rebuild(targets) ch.Rebuild(targets)
keys := make([]string, 100) keys := make([]string, 100)
for i := 0; i < 100; i++ { for i := range 100 {
keys[i] = fmt.Sprintf("192.168.1.%d", i) keys[i] = fmt.Sprintf("192.168.1.%d", i)
} }
@ -284,7 +284,7 @@ func BenchmarkIPHashSelect(b *testing.B) {
iph := NewIPHash() iph := NewIPHash()
ips := make([]string, 100) ips := make([]string, 100)
for i := 0; i < 100; i++ { for i := range 100 {
ips[i] = fmt.Sprintf("192.168.1.%d", i) ips[i] = fmt.Sprintf("192.168.1.%d", i)
} }

View File

@ -124,12 +124,10 @@ func TestRoundRobin_Select(t *testing.T) {
} }
var wg sync.WaitGroup var wg sync.WaitGroup
for i := 0; i < 100; i++ { for range 100 {
wg.Add(1) wg.Go(func() {
go func() {
defer wg.Done()
_ = rr.Select(targets) _ = rr.Select(targets)
}() })
} }
wg.Wait() wg.Wait()
}) })
@ -148,7 +146,7 @@ func TestWeightedRoundRobin_Select(t *testing.T) {
// 统计选择次数 // 统计选择次数
counts := make(map[string]int) counts := make(map[string]int)
for i := 0; i < 400; i++ { for range 400 {
got := wrr.Select(targets) got := wrr.Select(targets)
if got == nil { if got == nil {
t.Fatal("Select() = nil, want non-nil") t.Fatal("Select() = nil, want non-nil")
@ -175,7 +173,7 @@ func TestWeightedRoundRobin_Select(t *testing.T) {
// 权重为0的目标应该被当作权重为1处理 // 权重为0的目标应该被当作权重为1处理
counts := make(map[string]int) counts := make(map[string]int)
for i := 0; i < 100; i++ { for range 100 {
got := wrr.Select(targets) got := wrr.Select(targets)
if got == nil { if got == nil {
t.Fatal("Select() = nil, want non-nil") t.Fatal("Select() = nil, want non-nil")
@ -225,7 +223,7 @@ func TestWeightedRoundRobin_Select(t *testing.T) {
targets[1].Healthy.Store(true) targets[1].Healthy.Store(true)
// 所有选择都应该落在健康目标上 // 所有选择都应该落在健康目标上
for i := 0; i < 50; i++ { for range 50 {
got := wrr.Select(targets) got := wrr.Select(targets)
if got == nil { if got == nil {
t.Fatal("Select() = nil, want non-nil") t.Fatal("Select() = nil, want non-nil")
@ -331,7 +329,7 @@ func TestIPHash_Select(t *testing.T) {
// 使用相同的IP地址多次选择 // 使用相同的IP地址多次选择
clientIP := "192.168.1.100" clientIP := "192.168.1.100"
var firstSelection *Target var firstSelection *Target
for i := 0; i < 10; i++ { for range 10 {
got := ih.SelectByIP(targets, clientIP) got := ih.SelectByIP(targets, clientIP)
if got == nil { if got == nil {
t.Fatal("SelectByIP() = nil, want non-nil") t.Fatal("SelectByIP() = nil, want non-nil")
@ -447,12 +445,10 @@ func TestConnectionsAtomic(t *testing.T) {
target.Healthy.Store(true) target.Healthy.Store(true)
var wg sync.WaitGroup var wg sync.WaitGroup
for i := 0; i < 1000; i++ { for range 1000 {
wg.Add(1) wg.Go(func() {
go func() {
defer wg.Done()
IncrementConnections(target) IncrementConnections(target)
}() })
} }
wg.Wait() wg.Wait()
@ -466,12 +462,10 @@ func TestConnectionsAtomic(t *testing.T) {
target.Healthy.Store(true) target.Healthy.Store(true)
var wg sync.WaitGroup var wg sync.WaitGroup
for i := 0; i < 1000; i++ { for range 1000 {
wg.Add(1) wg.Go(func() {
go func() {
defer wg.Done()
DecrementConnections(target) DecrementConnections(target)
}() })
} }
wg.Wait() wg.Wait()
@ -486,20 +480,16 @@ func TestConnectionsAtomic(t *testing.T) {
var wg sync.WaitGroup var wg sync.WaitGroup
// 500个增加 // 500个增加
for i := 0; i < 500; i++ { for range 500 {
wg.Add(1) wg.Go(func() {
go func() {
defer wg.Done()
IncrementConnections(target) IncrementConnections(target)
}() })
} }
// 300个减少 // 300个减少
for i := 0; i < 300; i++ { for range 300 {
wg.Add(1) wg.Go(func() {
go func() {
defer wg.Done()
DecrementConnections(target) DecrementConnections(target)
}() })
} }
wg.Wait() wg.Wait()
@ -727,7 +717,7 @@ func TestConsistentHash(t *testing.T) {
// 相同的键应该选择相同的目标 // 相同的键应该选择相同的目标
key := "192.168.1.100" key := "192.168.1.100"
first := ch.SelectByKey(targets, key) first := ch.SelectByKey(targets, key)
for i := 0; i < 10; i++ { for range 10 {
got := ch.SelectByKey(targets, key) got := ch.SelectByKey(targets, key)
if got == nil { if got == nil {
t.Fatal("SelectByKey() = nil") t.Fatal("SelectByKey() = nil")
@ -814,7 +804,7 @@ func TestConsistentHashSelectExcludingByKey(t *testing.T) {
key := "192.168.1.100" key := "192.168.1.100"
// 多次选择,验证不会选中排除的目标 // 多次选择,验证不会选中排除的目标
for i := 0; i < 100; i++ { for range 100 {
got := ch.SelectExcludingByKey(targets, excluded, key) got := ch.SelectExcludingByKey(targets, excluded, key)
if got == nil { if got == nil {
t.Fatal("SelectExcludingByKey() = nil, want non-nil") t.Fatal("SelectExcludingByKey() = nil, want non-nil")
@ -877,17 +867,15 @@ func TestConsistentHashSelectExcludingByKey(t *testing.T) {
key := "192.168.1.100" key := "192.168.1.100"
var wg sync.WaitGroup var wg sync.WaitGroup
for i := 0; i < 100; i++ { for range 100 {
wg.Add(1) wg.Go(func() {
go func() { for range 100 {
defer wg.Done()
for j := 0; j < 100; j++ {
got := ch.SelectExcludingByKey(targets, excluded, key) got := ch.SelectExcludingByKey(targets, excluded, key)
if got != nil && got.URL == targets[0].URL { if got != nil && got.URL == targets[0].URL {
t.Errorf("并发时选中了被排除的目标: %q", got.URL) t.Errorf("并发时选中了被排除的目标: %q", got.URL)
} }
} }
}() })
} }
wg.Wait() wg.Wait()
}) })
@ -906,7 +894,7 @@ func TestConsistentHashSelectExcludingByKey(t *testing.T) {
// 相同键应该始终返回相同的目标 // 相同键应该始终返回相同的目标
var firstSelection *Target var firstSelection *Target
for i := 0; i < 50; i++ { for range 50 {
got := ch.SelectExcludingByKey(targets, excluded, key) got := ch.SelectExcludingByKey(targets, excluded, key)
if got == nil { if got == nil {
t.Fatal("SelectExcludingByKey() = nil, want non-nil") t.Fatal("SelectExcludingByKey() = nil, want non-nil")
@ -1021,15 +1009,13 @@ func TestRoundRobin_SelectExcluding(t *testing.T) {
excluded := []*Target{targets[0]} excluded := []*Target{targets[0]}
var wg sync.WaitGroup var wg sync.WaitGroup
for i := 0; i < 100; i++ { for range 100 {
wg.Add(1) wg.Go(func() {
go func() {
defer wg.Done()
got := rr.SelectExcluding(targets, excluded) got := rr.SelectExcluding(targets, excluded)
if got != nil && got.URL == targets[0].URL { if got != nil && got.URL == targets[0].URL {
t.Errorf("选中了被排除的目标: %q", got.URL) t.Errorf("选中了被排除的目标: %q", got.URL)
} }
}() })
} }
wg.Wait() wg.Wait()
}) })
@ -1066,7 +1052,7 @@ func TestWeightedRoundRobin_SelectExcluding(t *testing.T) {
excluded := []*Target{targets[1]} excluded := []*Target{targets[1]}
// 排除高权重目标后,只应选低权重目标 // 排除高权重目标后,只应选低权重目标
for i := 0; i < 20; i++ { for range 20 {
got := wrr.SelectExcluding(targets, excluded) got := wrr.SelectExcluding(targets, excluded)
if got == nil { if got == nil {
t.Fatal("SelectExcluding() = nil, want non-nil") t.Fatal("SelectExcluding() = nil, want non-nil")
@ -1518,15 +1504,13 @@ func TestLeastConnections_ConcurrentSelection(t *testing.T) {
lc := NewLeastConnections() lc := NewLeastConnections()
var wg sync.WaitGroup var wg sync.WaitGroup
for i := 0; i < 100; i++ { for range 100 {
wg.Add(1) wg.Go(func() {
go func() {
defer wg.Done()
got := lc.Select(targets) got := lc.Select(targets)
if got == nil { if got == nil {
t.Error("并发Select() = nil, want non-nil") t.Error("并发Select() = nil, want non-nil")
} }
}() })
} }
wg.Wait() wg.Wait()
} }
@ -1542,15 +1526,13 @@ func TestWeightedRoundRobin_ConcurrentSelection(t *testing.T) {
wrr := NewWeightedRoundRobin() wrr := NewWeightedRoundRobin()
var wg sync.WaitGroup var wg sync.WaitGroup
for i := 0; i < 100; i++ { for range 100 {
wg.Add(1) wg.Go(func() {
go func() {
defer wg.Done()
got := wrr.Select(targets) got := wrr.Select(targets)
if got == nil { if got == nil {
t.Error("并发Select() = nil, want non-nil") t.Error("并发Select() = nil, want non-nil")
} }
}() })
} }
wg.Wait() wg.Wait()
} }
@ -1564,7 +1546,7 @@ func TestIPHash_ConcurrentSelection(t *testing.T) {
ih := NewIPHash() ih := NewIPHash()
var wg sync.WaitGroup var wg sync.WaitGroup
for i := 0; i < 100; i++ { for i := range 100 {
wg.Add(1) wg.Add(1)
go func(ip string) { go func(ip string) {
defer wg.Done() defer wg.Done()
@ -1691,7 +1673,7 @@ func TestWeightedRoundRobin_NegativeWeight(t *testing.T) {
// 负权重要被当作1处理 // 负权重要被当作1处理
counts := make(map[string]int) counts := make(map[string]int)
for i := 0; i < 100; i++ { for range 100 {
got := wrr.Select(targets) got := wrr.Select(targets)
if got == nil { if got == nil {
t.Fatal("Select() = nil, want non-nil") t.Fatal("Select() = nil, want non-nil")
@ -1885,7 +1867,7 @@ func TestConsistentHash_Select(t *testing.T) {
// 多次调用应该返回相同结果(空键的一致性) // 多次调用应该返回相同结果(空键的一致性)
first := ch.Select(targets) first := ch.Select(targets)
for i := 0; i < 10; i++ { for range 10 {
got := ch.Select(targets) got := ch.Select(targets)
if got == nil { if got == nil {
t.Fatal("Select() = nil, want non-nil") t.Fatal("Select() = nil, want non-nil")
@ -2013,7 +1995,7 @@ func TestRandomBalancer(t *testing.T) {
b := NewRandom() b := NewRandom()
// 多次选择,验证总是选择连接数少的目标 // 多次选择,验证总是选择连接数少的目标
for i := 0; i < 100; i++ { for range 100 {
selected := b.Select(targets) selected := b.Select(targets)
if selected == nil { if selected == nil {
t.Error("should select a target") t.Error("should select a target")
@ -2037,7 +2019,7 @@ func TestRandomBalancer(t *testing.T) {
// 连接数相等时,两个目标都应该被选中 // 连接数相等时,两个目标都应该被选中
counts := make(map[string]int) counts := make(map[string]int)
for i := 0; i < 100; i++ { for range 100 {
selected := b.Select(targets) selected := b.Select(targets)
if selected != nil { if selected != nil {
counts[selected.URL]++ counts[selected.URL]++

View File

@ -17,6 +17,7 @@ package loadbalance
import ( import (
"fmt" "fmt"
"hash/fnv" "hash/fnv"
"slices"
"sort" "sort"
"sync" "sync"
) )
@ -138,9 +139,7 @@ func (c *ConsistentHash) rebuildCircle(targets []*Target) {
} }
// 排序哈希值 // 排序哈希值
sort.Slice(c.sortedHashes, func(i, j int) bool { slices.Sort(c.sortedHashes)
return c.sortedHashes[i] < c.sortedHashes[j]
})
} }
// hashKeyString 计算字符串的哈希值(使用 FNV-64a // hashKeyString 计算字符串的哈希值(使用 FNV-64a

View File

@ -13,6 +13,7 @@
package loadbalance package loadbalance
import ( import (
"maps"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
@ -143,13 +144,7 @@ func (m *SlowStartManager) updateEffectiveWeights() {
// 线性增长:从 1 增加到 BaseWeight // 线性增长:从 1 增加到 BaseWeight
progress := float64(elapsed) / float64(state.SlowStart) progress := float64(elapsed) / float64(state.SlowStart)
effectiveWeight := int(1 + progress*float64(state.BaseWeight-1)) effectiveWeight := min(max(int(1+progress*float64(state.BaseWeight-1)), 1), state.BaseWeight)
if effectiveWeight < 1 {
effectiveWeight = 1
}
if effectiveWeight > state.BaseWeight {
effectiveWeight = state.BaseWeight
}
// 查找目标并更新 EffectiveWeight // 查找目标并更新 EffectiveWeight
if m.findTarget != nil { if m.findTarget != nil {
@ -184,8 +179,6 @@ func (m *SlowStartManager) GetAllStates() map[string]*SlowStartState {
defer m.mu.RUnlock() defer m.mu.RUnlock()
result := make(map[string]*SlowStartState, len(m.targets)) result := make(map[string]*SlowStartState, len(m.targets))
for k, v := range m.targets { maps.Copy(result, m.targets)
result[k] = v
}
return result return result
} }

View File

@ -81,7 +81,7 @@ func (m *LocationManager) Register(location string, handler fasthttp.RequestHand
// 返回值: // 返回值:
// - *LocationCaptureResult: 子请求响应结果 // - *LocationCaptureResult: 子请求响应结果
// - error: 当前实现始终返回 nil // - error: 当前实现始终返回 nil
func (m *LocationManager) Capture(parentCtx *fasthttp.RequestCtx, location string, opts map[string]interface{}) (*LocationCaptureResult, error) { func (m *LocationManager) Capture(parentCtx *fasthttp.RequestCtx, location string, opts map[string]any) (*LocationCaptureResult, error) {
m.mu.Lock() m.mu.Lock()
handler, ok := m.handlers[location] handler, ok := m.handlers[location]
m.mu.Unlock() m.mu.Unlock()
@ -175,7 +175,7 @@ func RegisterLocationAPI(L *glua.LState, manager *LocationManager, ngx *glua.LTa
uri := L.CheckString(1) uri := L.CheckString(1)
// 解析选项 // 解析选项
opts := make(map[string]interface{}) opts := make(map[string]any)
if L.GetTop() >= 2 { if L.GetTop() >= 2 {
optionsTable := L.CheckTable(2) optionsTable := L.CheckTable(2)
optionsTable.ForEach(func(key, value glua.LValue) { optionsTable.ForEach(func(key, value glua.LValue) {

View File

@ -78,7 +78,7 @@ func TestLocationManagerCaptureWithOptions(t *testing.T) {
parentCtx.Request.SetRequestURI("/parent") parentCtx.Request.SetRequestURI("/parent")
// 使用自定义选项 // 使用自定义选项
opts := map[string]interface{}{ opts := map[string]any{
"method": "POST", "method": "POST",
"body": "test body", "body": "test body",
} }

View File

@ -70,7 +70,7 @@ type TCPSocket struct {
mu sync.RWMutex mu sync.RWMutex
// closed 关闭标记(原子操作) // closed 关闭标记(原子操作)
closed int32 closed atomic.Int32
} }
// NewTCPSocket 创建新的 TCP socket 实例。 // NewTCPSocket 创建新的 TCP socket 实例。
@ -416,7 +416,7 @@ func (s *TCPSocket) ReceiveUntil(pattern string, inclusive bool) ([]byte, error)
// 检查是否匹配模式 // 检查是否匹配模式
if len(result) >= patternLen { if len(result) >= patternLen {
matched := true matched := true
for i := 0; i < patternLen; i++ { for i := range patternLen {
if result[len(result)-patternLen+i] != patternBytes[i] { if result[len(result)-patternLen+i] != patternBytes[i] {
matched = false matched = false
break break
@ -442,7 +442,7 @@ func (s *TCPSocket) Close() error {
if s == nil { if s == nil {
return nil return nil
} }
if !atomic.CompareAndSwapInt32(&s.closed, 0, 1) { if !s.closed.CompareAndSwap(0, 1) {
return nil // 已经关闭 return nil // 已经关闭
} }
@ -507,7 +507,7 @@ func (s *TCPSocket) setState(state SocketState) {
// IsClosed 检查是否已关闭 // IsClosed 检查是否已关闭
func (s *TCPSocket) IsClosed() bool { func (s *TCPSocket) IsClosed() bool {
return atomic.LoadInt32(&s.closed) == 1 return s.closed.Load() == 1
} }
// LocalAddr 获取本地地址 // LocalAddr 获取本地地址

View File

@ -67,7 +67,7 @@ type TimerManager struct {
schedulerL *glua.LState schedulerL *glua.LState
// nextID 下一个定时器 ID原子操作 // nextID 下一个定时器 ID原子操作
nextID uint64 nextID atomic.Uint64
// mu 定时器映射读写锁 // mu 定时器映射读写锁
mu sync.Mutex mu sync.Mutex
@ -76,10 +76,10 @@ type TimerManager struct {
queueMu sync.Mutex queueMu sync.Mutex
// active 活跃定时器计数(原子操作) // active 活跃定时器计数(原子操作)
active int32 active atomic.Int32
// stopping 停止标记(原子操作) // stopping 停止标记(原子操作)
stopping int32 stopping atomic.Int32
// queueClosed 队列是否已关闭 // queueClosed 队列是否已关闭
queueClosed bool queueClosed bool
@ -181,14 +181,14 @@ func NewTimerManager(engine *LuaEngine) *TimerManager {
// - 回调不能捕获 upvalue闭包变量因为它们会在不同 goroutine 中执行 // - 回调不能捕获 upvalue闭包变量因为它们会在不同 goroutine 中执行
// - 跨协程数据共享应使用 shared dict // - 跨协程数据共享应使用 shared dict
func (m *TimerManager) At(delay time.Duration, callback *glua.LFunction, args []glua.LValue) (*TimerHandle, error) { func (m *TimerManager) At(delay time.Duration, callback *glua.LFunction, args []glua.LValue) (*TimerHandle, error) {
if atomic.LoadInt32(&m.stopping) != 0 { if m.stopping.Load() != 0 {
return nil, nil // 服务器正在关闭,不接受新定时器 return nil, nil // 服务器正在关闭,不接受新定时器
} }
m.mu.Lock() m.mu.Lock()
defer m.mu.Unlock() defer m.mu.Unlock()
id := atomic.AddUint64(&m.nextID, 1) id := m.nextID.Add(1)
// 编译回调为 FunctionProto // 编译回调为 FunctionProto
var proto *glua.FunctionProto var proto *glua.FunctionProto
@ -216,7 +216,7 @@ func (m *TimerManager) At(delay time.Duration, callback *glua.LFunction, args []
}) })
m.timers[id] = entry m.timers[id] = entry
atomic.AddInt32(&m.active, 1) m.active.Add(1)
return &TimerHandle{id: id, manager: m}, nil return &TimerHandle{id: id, manager: m}, nil
} }
@ -227,7 +227,7 @@ func (m *TimerManager) At(delay time.Duration, callback *glua.LFunction, args []
// 检查取消信号,清理条目,并在队列满时丢弃回调。 // 检查取消信号,清理条目,并在队列满时丢弃回调。
func (m *TimerManager) executeTimer(entry *TimerEntry) { func (m *TimerManager) executeTimer(entry *TimerEntry) {
defer func() { defer func() {
atomic.AddInt32(&m.active, -1) m.active.Add(-1)
close(entry.done) close(entry.done)
}() }()
@ -319,7 +319,7 @@ func (m *TimerManager) Cancel(handle *TimerHandle) bool {
// 清理 // 清理
delete(m.timers, entry.id) delete(m.timers, entry.id)
atomic.AddInt32(&m.active, -1) m.active.Add(-1)
return true return true
} }
@ -336,11 +336,11 @@ func (m *TimerManager) Cancel(handle *TimerHandle) bool {
// - bool: true 表示所有定时器已正常完成false 表示超时 // - bool: true 表示所有定时器已正常完成false 表示超时
func (m *TimerManager) WaitAll(timeout time.Duration) bool { func (m *TimerManager) WaitAll(timeout time.Duration) bool {
// 设置停止标志 // 设置停止标志
atomic.StoreInt32(&m.stopping, 1) m.stopping.Store(1)
// 等待所有定时器完成 // 等待所有定时器完成
start := time.Now() start := time.Now()
for atomic.LoadInt32(&m.active) > 0 { for m.active.Load() > 0 {
if time.Since(start) > timeout { if time.Since(start) > timeout {
// 超时,强制取消所有 // 超时,强制取消所有
m.mu.Lock() m.mu.Lock()
@ -369,12 +369,12 @@ func (m *TimerManager) WaitAll(timeout time.Duration) bool {
// //
// 注意:该方法是幂等的,可安全调用多次。 // 注意:该方法是幂等的,可安全调用多次。
func (m *TimerManager) Close() { func (m *TimerManager) Close() {
if m == nil || atomic.LoadInt32(&m.stopping) != 0 { if m == nil || m.stopping.Load() != 0 {
return // 已关闭或 nil return // 已关闭或 nil
} }
// 1. 停止接受新定时器 // 1. 停止接受新定时器
atomic.StoreInt32(&m.stopping, 1) m.stopping.Store(1)
// 2. 优雅关闭:等待回调队列排空 // 2. 优雅关闭:等待回调队列排空
m.gracefulShutdown(5 * time.Second) m.gracefulShutdown(5 * time.Second)
@ -415,7 +415,7 @@ func (m *TimerManager) gracefulShutdown(timeout time.Duration) {
// 返回值: // 返回值:
// - int32: 活跃定时器数量 // - int32: 活跃定时器数量
func (m *TimerManager) ActiveCount() int32 { func (m *TimerManager) ActiveCount() int32 {
return atomic.LoadInt32(&m.active) return m.active.Load()
} }
// RegisterTimerAPI 注册 ngx.timer API 到 Lua 状态机。 // RegisterTimerAPI 注册 ngx.timer API 到 Lua 状态机。

View File

@ -324,11 +324,11 @@ func TestBoundarySharedDictConcurrentAccess(t *testing.T) {
concurrency := 10 concurrency := 10
iterations := 100 iterations := 100
for i := 0; i < concurrency; i++ { for i := range concurrency {
wg.Add(1) wg.Add(1)
go func(id int) { go func(id int) {
defer wg.Done() defer wg.Done()
for j := 0; j < iterations; j++ { for range iterations {
key := "key-" + string(rune('0'+id%10)) key := "key-" + string(rune('0'+id%10))
_, _ = dict.Set(key, "value", 0) _, _ = dict.Set(key, "value", 0)
_, _, _ = dict.Get(key) _, _, _ = dict.Get(key)

View File

@ -87,10 +87,10 @@ type CodeCache struct {
ttl time.Duration ttl time.Duration
// 缓存命中次数 // 缓存命中次数
hits uint64 hits atomic.Uint64
// 缓存未命中次数 // 缓存未命中次数
misses uint64 misses atomic.Uint64
// 读写锁 // 读写锁
mu sync.RWMutex mu sync.RWMutex
@ -156,12 +156,12 @@ func (c *CodeCache) GetOrCompileInline(src string) (*glua.FunctionProto, error)
c.mu.RUnlock() c.mu.RUnlock()
if ok && !c.isExpired(cached) { if ok && !c.isExpired(cached) {
atomic.AddUint64(&c.hits, 1) c.hits.Add(1)
cached.AccessAt.Store(time.Now()) cached.AccessAt.Store(time.Now())
return cached.Proto, nil return cached.Proto, nil
} }
atomic.AddUint64(&c.misses, 1) c.misses.Add(1)
// 编译脚本 // 编译脚本
chunk, err := parse.Parse(strings.NewReader(src), "<inline>") chunk, err := parse.Parse(strings.NewReader(src), "<inline>")
@ -210,12 +210,12 @@ func (c *CodeCache) GetOrCompileFile(path string) (*glua.FunctionProto, error) {
// 检查是否需要重新加载 // 检查是否需要重新加载
if ok && !c.isExpired(cached) && !c.isFileChanged(cached) { if ok && !c.isExpired(cached) && !c.isFileChanged(cached) {
atomic.AddUint64(&c.hits, 1) c.hits.Add(1)
cached.AccessAt.Store(time.Now()) cached.AccessAt.Store(time.Now())
return cached.Proto, nil return cached.Proto, nil
} }
atomic.AddUint64(&c.misses, 1) c.misses.Add(1)
// 读取文件 // 读取文件
content, err := os.ReadFile(path) content, err := os.ReadFile(path)
@ -345,13 +345,13 @@ func (c *CodeCache) isFileChanged(cached *CachedProto) bool {
func (c *CodeCache) Stats() (hits, misses uint64, size int) { func (c *CodeCache) Stats() (hits, misses uint64, size int) {
c.mu.RLock() c.mu.RLock()
defer c.mu.RUnlock() defer c.mu.RUnlock()
return atomic.LoadUint64(&c.hits), atomic.LoadUint64(&c.misses), len(c.protos) return c.hits.Load(), c.misses.Load(), len(c.protos)
} }
// HitRate 返回缓存命中率 // HitRate 返回缓存命中率
func (c *CodeCache) HitRate() float64 { func (c *CodeCache) HitRate() float64 {
hits := atomic.LoadUint64(&c.hits) hits := c.hits.Load()
misses := atomic.LoadUint64(&c.misses) misses := c.misses.Load()
total := hits + misses total := hits + misses
if total == 0 { if total == 0 {
return 0 return 0

View File

@ -90,7 +90,7 @@ type LuaEngine struct {
// 并发控制 // 并发控制
maxCoroutines int maxCoroutines int
activeCount int32 activeCount atomic.Int32
// 引擎统计 // 引擎统计
stats EngineStats stats EngineStats
@ -181,7 +181,7 @@ func NewEngine(config *Config) (*LuaEngine, error) {
cancel: cancel, cancel: cancel,
sharedDictManager: NewSharedDictManager(), sharedDictManager: NewSharedDictManager(),
coroutinePool: sync.Pool{ coroutinePool: sync.Pool{
New: func() interface{} { New: func() any {
// 注意:这里只是创建空的协程对象结构 // 注意:这里只是创建空的协程对象结构
// 实际的协程通过 L.NewThread() 创建 // 实际的协程通过 L.NewThread() 创建
return &LuaCoroutine{} return &LuaCoroutine{}
@ -262,9 +262,9 @@ func (e *LuaEngine) Close() {
// - 使用完毕后必须调用 Close() 或 releaseCoroutine() 释放资源 // - 使用完毕后必须调用 Close() 或 releaseCoroutine() 释放资源
func (e *LuaEngine) NewCoroutine(req *fasthttp.RequestCtx) (*LuaCoroutine, error) { func (e *LuaEngine) NewCoroutine(req *fasthttp.RequestCtx) (*LuaCoroutine, error) {
// 步骤1: 检查并发限制 // 步骤1: 检查并发限制
current := atomic.AddInt32(&e.activeCount, 1) current := e.activeCount.Add(1)
if current > int32(e.maxCoroutines) { if current > int32(e.maxCoroutines) {
atomic.AddInt32(&e.activeCount, -1) e.activeCount.Add(-1)
return nil, fmt.Errorf("max concurrent coroutines exceeded: %d/%d", current, e.maxCoroutines) return nil, fmt.Errorf("max concurrent coroutines exceeded: %d/%d", current, e.maxCoroutines)
} }
@ -272,7 +272,7 @@ func (e *LuaEngine) NewCoroutine(req *fasthttp.RequestCtx) (*LuaCoroutine, error
// 协程继承主 LState 的全局环境 // 协程继承主 LState 的全局环境
co, cancel := e.L.NewThread() co, cancel := e.L.NewThread()
if co == nil { if co == nil {
atomic.AddInt32(&e.activeCount, -1) e.activeCount.Add(-1)
return nil, fmt.Errorf("failed to create coroutine") return nil, fmt.Errorf("failed to create coroutine")
} }
@ -330,7 +330,7 @@ func (e *LuaEngine) releaseCoroutine(coro *LuaCoroutine) {
coro.executionCancel = nil coro.executionCancel = nil
// 步骤4: 更新计数 // 步骤4: 更新计数
atomic.AddInt32(&e.activeCount, -1) e.activeCount.Add(-1)
atomic.AddUint64(&e.stats.CoroutinesClosed, 1) atomic.AddUint64(&e.stats.CoroutinesClosed, 1)
// 步骤5: 放回池中(仅复用 LuaCoroutine 结构体内存) // 步骤5: 放回池中(仅复用 LuaCoroutine 结构体内存)
@ -365,7 +365,7 @@ func (e *LuaEngine) Stats() EngineStats {
// 返回值: // 返回值:
// - int32: 当前正在执行的协程数 // - int32: 当前正在执行的协程数
func (e *LuaEngine) ActiveCoroutines() int32 { func (e *LuaEngine) ActiveCoroutines() int32 {
return atomic.LoadInt32(&e.activeCount) return e.activeCount.Load()
} }
// SharedDictManager 返回共享字典管理器实例。 // SharedDictManager 返回共享字典管理器实例。

View File

@ -244,14 +244,14 @@ func TestDelayedResponseWriter_Pool(t *testing.T) {
ctx := mockRequestCtx() ctx := mockRequestCtx()
// 预热池 // 预热池
for i := 0; i < 100; i++ { for range 100 {
ri := AcquireResponseInterceptor(ctx) ri := AcquireResponseInterceptor(ctx)
ReleaseResponseInterceptor(ri) ReleaseResponseInterceptor(ri)
} }
// 测试从池获取的性能 // 测试从池获取的性能
start := time.Now() start := time.Now()
for i := 0; i < 10000; i++ { for range 10000 {
ri := AcquireResponseInterceptor(ctx) ri := AcquireResponseInterceptor(ctx)
ri.WriteString("test") ri.WriteString("test")
ReleaseResponseInterceptor(ri) ReleaseResponseInterceptor(ri)
@ -270,7 +270,7 @@ func TestConcurrentAccess(t *testing.T) {
var wg sync.WaitGroup var wg sync.WaitGroup
errors := make(chan error, 100) errors := make(chan error, 100)
for i := 0; i < 100; i++ { for i := range 100 {
wg.Add(1) wg.Add(1)
go func(idx int) { go func(idx int) {
defer wg.Done() defer wg.Done()
@ -464,7 +464,7 @@ func TestBodyFilterPhase(t *testing.T) {
name: "large body", name: "large body",
inputBody: strings.Repeat("x", 10000), inputBody: strings.Repeat("x", 10000),
filterFunc: func(b []byte) []byte { filterFunc: func(b []byte) []byte {
return append([]byte("size="), []byte(fmt.Sprintf("%d ", len(b)))...) return append([]byte("size="), fmt.Appendf(nil, "%d ", len(b))...)
}, },
expectedOutput: "size=10000 ", expectedOutput: "size=10000 ",
}, },
@ -501,7 +501,7 @@ func TestFilterPhaseSuccessRate(t *testing.T) {
successCount := 0 successCount := 0
var mu sync.Mutex var mu sync.Mutex
for i := 0; i < totalRequests; i++ { for i := range totalRequests {
ctx := mockRequestCtx() ctx := mockRequestCtx()
drw := NewDelayedResponseWriter(ctx) drw := NewDelayedResponseWriter(ctx)
drw.EnableFilterPhase() drw.EnableFilterPhase()
@ -537,14 +537,14 @@ func TestPerformanceOverhead(t *testing.T) {
// 基准:正常写入 // 基准:正常写入
ctx1 := mockRequestCtx() ctx1 := mockRequestCtx()
start := time.Now() start := time.Now()
for i := 0; i < 10000; i++ { for range 10000 {
ctx1.Response.SetBodyString("Hello, World!") ctx1.Response.SetBodyString("Hello, World!")
} }
baselineDuration := time.Since(start) baselineDuration := time.Since(start)
// 测试:延迟写入 // 测试:延迟写入
start = time.Now() start = time.Now()
for i := 0; i < 10000; i++ { for range 10000 {
ctx := mockRequestCtx() ctx := mockRequestCtx()
drw := NewDelayedResponseWriter(ctx) drw := NewDelayedResponseWriter(ctx)
drw.EnableFilterPhase() drw.EnableFilterPhase()
@ -896,7 +896,7 @@ func TestFilterPhaseFeasibility(t *testing.T) {
const iterations = 100 const iterations = 100
success := 0 success := 0
for i := 0; i < iterations; i++ { for range iterations {
ctx := mockRequestCtx() ctx := mockRequestCtx()
drw := NewDelayedResponseWriter(ctx) drw := NewDelayedResponseWriter(ctx)
drw.EnableFilterPhase() drw.EnableFilterPhase()
@ -963,7 +963,7 @@ func TestFilterPhaseFeasibility(t *testing.T) {
// 基准 // 基准
start := time.Now() start := time.Now()
for i := 0; i < iterations; i++ { for range iterations {
ctx := mockRequestCtx() ctx := mockRequestCtx()
ctx.Response.SetBodyString("test") ctx.Response.SetBodyString("test")
} }
@ -971,7 +971,7 @@ func TestFilterPhaseFeasibility(t *testing.T) {
// 延迟写入 // 延迟写入
start = time.Now() start = time.Now()
for i := 0; i < iterations; i++ { for range iterations {
ctx := mockRequestCtx() ctx := mockRequestCtx()
drw := NewDelayedResponseWriter(ctx) drw := NewDelayedResponseWriter(ctx)
drw.EnableFilterPhase() drw.EnableFilterPhase()
@ -1019,7 +1019,7 @@ func TestFilterPhaseMetrics(t *testing.T) {
const iterations = 100 const iterations = 100
start := time.Now() start := time.Now()
for i := 0; i < iterations; i++ { for i := range iterations {
ctx := mockRequestCtx() ctx := mockRequestCtx()
drw := NewDelayedResponseWriter(ctx) drw := NewDelayedResponseWriter(ctx)
drw.EnableFilterPhase() drw.EnableFilterPhase()
@ -1177,13 +1177,11 @@ func BenchmarkFilterPhaseScalability(b *testing.B) {
b.Run(fmt.Sprintf("goroutines-%d", goroutines), func(b *testing.B) { b.Run(fmt.Sprintf("goroutines-%d", goroutines), func(b *testing.B) {
var wg sync.WaitGroup var wg sync.WaitGroup
errors := make(chan error, b.N) errors := make(chan error, b.N)
var completed int32 var completed atomic.Int32
b.ResetTimer() b.ResetTimer()
for i := 0; i < goroutines; i++ { for range goroutines {
wg.Add(1) wg.Go(func() {
go func() {
defer wg.Done()
for j := 0; j < b.N/goroutines; j++ { for j := 0; j < b.N/goroutines; j++ {
ctx := mockRequestCtx() ctx := mockRequestCtx()
drw := NewDelayedResponseWriter(ctx) drw := NewDelayedResponseWriter(ctx)
@ -1193,10 +1191,10 @@ func BenchmarkFilterPhaseScalability(b *testing.B) {
if err := drw.Flush(); err != nil { if err := drw.Flush(); err != nil {
errors <- err errors <- err
} else { } else {
atomic.AddInt32(&completed, 1) completed.Add(1)
} }
} }
}() })
} }
wg.Wait() wg.Wait()
close(errors) close(errors)
@ -1373,7 +1371,7 @@ func TestFinalVerification(t *testing.T) {
const total = 1000 const total = 1000
success := 0 success := 0
for i := 0; i < total; i++ { for range total {
ctx := mockRequestCtx() ctx := mockRequestCtx()
drw := NewDelayedResponseWriter(ctx) drw := NewDelayedResponseWriter(ctx)
drw.EnableFilterPhase() drw.EnableFilterPhase()
@ -1439,7 +1437,7 @@ func TestFinalVerification(t *testing.T) {
// 基准 // 基准
start := time.Now() start := time.Now()
for i := 0; i < iterations; i++ { for range iterations {
ctx := mockRequestCtx() ctx := mockRequestCtx()
ctx.Response.SetBodyString("test") ctx.Response.SetBodyString("test")
ctx.Response.Header.Set("X-Test", "value") ctx.Response.Header.Set("X-Test", "value")
@ -1448,7 +1446,7 @@ func TestFinalVerification(t *testing.T) {
// Filter phase // Filter phase
start = time.Now() start = time.Now()
for i := 0; i < iterations; i++ { for range iterations {
ctx := mockRequestCtx() ctx := mockRequestCtx()
drw := NewDelayedResponseWriter(ctx) drw := NewDelayedResponseWriter(ctx)
drw.EnableFilterPhase() drw.EnableFilterPhase()

View File

@ -432,7 +432,7 @@ func (drw *DelayedResponseWriter) DelHeader(key string) {
// ResponseInterceptorPool 响应拦截器对象池。 // ResponseInterceptorPool 响应拦截器对象池。
var ResponseInterceptorPool = sync.Pool{ var ResponseInterceptorPool = sync.Pool{
New: func() interface{} { New: func() any {
return &ResponseInterceptor{} return &ResponseInterceptor{}
}, },
} }
@ -666,7 +666,7 @@ func (drw *DelayedResponseWriter) Redirect(uri string, statusCode int) {
// bufferPool body 缓冲区对象池。 // bufferPool body 缓冲区对象池。
var bufferPool = sync.Pool{ var bufferPool = sync.Pool{
New: func() interface{} { New: func() any {
buf := make([]byte, 0, 4096) // 4KB 初始容量 buf := make([]byte, 0, 4096) // 4KB 初始容量
return &buf return &buf
}, },
@ -747,10 +747,7 @@ func (bw *BufferedWriter) Write(p []byte) (int, error) {
// 检查是否需要扩容 // 检查是否需要扩容
if len(bw.buf)+len(p) > cap(bw.buf) { if len(bw.buf)+len(p) > cap(bw.buf) {
// 扩容 // 扩容
newCap := cap(bw.buf) * 2 newCap := max(cap(bw.buf)*2, len(bw.buf)+len(p))
if newCap < len(bw.buf)+len(p) {
newCap = len(bw.buf) + len(p)
}
newBuf := make([]byte, len(bw.buf), newCap) newBuf := make([]byte, len(bw.buf), newCap)
copy(newBuf, bw.buf) copy(newBuf, bw.buf)
releaseBuffer(bw.buf) releaseBuffer(bw.buf)

View File

@ -186,7 +186,7 @@ func BenchmarkTimerGracefulShutdown(b *testing.B) {
}) })
// 创建一些定时器 // 创建一些定时器
for j := 0; j < 10; j++ { for range 10 {
manager.At(1*time.Millisecond, callback, nil) manager.At(1*time.Millisecond, callback, nil)
} }

View File

@ -136,7 +136,7 @@ func TestLuaContextPoolMultipleReuse(t *testing.T) {
defer engine.Close() defer engine.Close()
// 循环多次 release/acquire验证状态始终正确 // 循环多次 release/acquire验证状态始终正确
for i := 0; i < 100; i++ { for range 100 {
ctx := NewContext(engine, nil) ctx := NewContext(engine, nil)
ctx.SetVariable("iter", "val") ctx.SetVariable("iter", "val")
ctx.Write([]byte("data")) ctx.Write([]byte("data"))

View File

@ -174,7 +174,7 @@ func TestLuaMiddlewarePerformanceOverhead(t *testing.T) {
// 测量 100 次执行的总时间 // 测量 100 次执行的总时间
iterations := 100 iterations := 100
start := time.Now() start := time.Now()
for i := 0; i < iterations; i++ { for range iterations {
ctx := &fasthttp.RequestCtx{} ctx := &fasthttp.RequestCtx{}
handler(ctx) handler(ctx)
} }

View File

@ -460,7 +460,7 @@ func TestSchedulerConcurrentAccess(t *testing.T) {
// 创建多个 LState 并发访问 // 创建多个 LState 并发访问
done := make(chan bool, 10) done := make(chan bool, 10)
for i := 0; i < 10; i++ { for range 10 {
go func() { go func() {
L := glua.NewState() L := glua.NewState()
defer L.Close() defer L.Close()
@ -477,7 +477,7 @@ func TestSchedulerConcurrentAccess(t *testing.T) {
} }
// 等待所有 goroutine 完成 // 等待所有 goroutine 完成
for i := 0; i < 10; i++ { for range 10 {
<-done <-done
} }
} }

View File

@ -116,7 +116,7 @@ type SocketOperation struct {
LastActivity time.Time LastActivity time.Time
// Result 操作结果 // Result 操作结果
Result interface{} Result any
// Error 操作错误 // Error 操作错误
Error error Error error
@ -125,7 +125,7 @@ type SocketOperation struct {
Done chan struct{} Done chan struct{}
// completed 原子标记1=已完成0=未完成 // completed 原子标记1=已完成0=未完成
completed int32 completed atomic.Int32
} }
// IsCompleted 检查操作是否已完成。 // IsCompleted 检查操作是否已完成。
@ -133,7 +133,7 @@ type SocketOperation struct {
// 返回值: // 返回值:
// - bool: true 表示已完成 // - bool: true 表示已完成
func (op *SocketOperation) IsCompleted() bool { func (op *SocketOperation) IsCompleted() bool {
return atomic.LoadInt32(&op.completed) == 1 return op.completed.Load() == 1
} }
// Complete 标记操作完成。 // Complete 标记操作完成。
@ -143,8 +143,8 @@ func (op *SocketOperation) IsCompleted() bool {
// 参数: // 参数:
// - result: 操作结果 // - result: 操作结果
// - err: 操作错误nil 表示成功) // - err: 操作错误nil 表示成功)
func (op *SocketOperation) Complete(result interface{}, err error) { func (op *SocketOperation) Complete(result any, err error) {
if atomic.CompareAndSwapInt32(&op.completed, 0, 1) { if op.completed.CompareAndSwap(0, 1) {
op.Result = result op.Result = result
op.Error = err op.Error = err
close(op.Done) close(op.Done)
@ -161,7 +161,7 @@ func (op *SocketOperation) Complete(result interface{}, err error) {
// 返回值: // 返回值:
// - interface{}: 操作结果 // - interface{}: 操作结果
// - error: 操作错误或上下文取消错误 // - error: 操作错误或上下文取消错误
func (op *SocketOperation) Wait(ctx context.Context) (interface{}, error) { func (op *SocketOperation) Wait(ctx context.Context) (any, error) {
select { select {
case <-op.Done: case <-op.Done:
return op.Result, op.Error return op.Result, op.Error
@ -319,7 +319,7 @@ func (cm *CosocketManager) StartOperation(socket *TCPSocket, opType OperationTyp
// - id: 操作 ID // - id: 操作 ID
// - result: 操作结果 // - result: 操作结果
// - err: 操作错误 // - err: 操作错误
func (cm *CosocketManager) CompleteOperation(id uint64, result interface{}, err error) { func (cm *CosocketManager) CompleteOperation(id uint64, result any, err error) {
cm.mu.Lock() cm.mu.Lock()
op, exists := cm.operations[id] op, exists := cm.operations[id]
if exists { if exists {

View File

@ -5,6 +5,8 @@
// 作者xfy // 作者xfy
package matcher package matcher
import "maps"
import "fmt" import "fmt"
// ConflictDetector 路径冲突检测器。 // ConflictDetector 路径冲突检测器。
@ -61,9 +63,7 @@ func (cd *ConflictDetector) Exists(path string) bool {
// - map[string]string: 路径到 location 类型的映射 // - map[string]string: 路径到 location 类型的映射
func (cd *ConflictDetector) GetRegisteredPaths() map[string]string { func (cd *ConflictDetector) GetRegisteredPaths() map[string]string {
result := make(map[string]string, len(cd.registeredPaths)) result := make(map[string]string, len(cd.registeredPaths))
for k, v := range cd.registeredPaths { maps.Copy(result, cd.registeredPaths)
result[k] = v
}
return result return result
} }

View File

@ -91,7 +91,7 @@ func BenchmarkBodyLimitPathMatching(b *testing.B) {
} }
// 添加大量路径配置 // 添加大量路径配置
for i := 0; i < 100; i++ { for i := range 100 {
path := "/api/v" + string(rune('0'+i%10)) + "/resource" + string(rune('0'+i%10)) path := "/api/v" + string(rune('0'+i%10)) + "/resource" + string(rune('0'+i%10))
size := "1mb" size := "1mb"
if i%3 == 0 { if i%3 == 0 {

View File

@ -260,13 +260,7 @@ func (r *slowReader) Read(p []byte) (n int, err error) {
// 每次只读取 chunkSize 字节 // 每次只读取 chunkSize 字节
remaining := len(r.data) - r.pos remaining := len(r.data) - r.pos
toRead := r.chunkSize toRead := min(min(r.chunkSize, remaining), len(p))
if toRead > remaining {
toRead = remaining
}
if toRead > len(p) {
toRead = len(p)
}
n = toRead n = toRead
copy(p, r.data[r.pos:r.pos+toRead]) copy(p, r.data[r.pos:r.pos+toRead])

View File

@ -13,6 +13,7 @@ package compression
import ( import (
"bytes" "bytes"
"io" "io"
"slices"
"testing" "testing"
"github.com/andybalholm/brotli" "github.com/andybalholm/brotli"
@ -82,13 +83,7 @@ func TestDefaultCompressibleTypes(t *testing.T) {
// 检查关键类型 // 检查关键类型
expected := []string{"text/html", "text/css", "application/json"} expected := []string{"text/html", "text/css", "application/json"}
for _, e := range expected { for _, e := range expected {
found := false found := slices.Contains(types, e)
for _, t := range types {
if t == e {
found = true
break
}
}
if !found { if !found {
t.Errorf("Expected type %s in default list", e) t.Errorf("Expected type %s in default list", e)
} }

View File

@ -429,14 +429,12 @@ func TestRateLimitedWriter_Concurrent(t *testing.T) {
writesPerGoroutine := 10 writesPerGoroutine := 10
data := []byte("test") data := []byte("test")
for i := 0; i < goroutines; i++ { for range goroutines {
wg.Add(1) wg.Go(func() {
go func() { for range writesPerGoroutine {
defer wg.Done()
for j := 0; j < writesPerGoroutine; j++ {
_, _ = w.Write(data) _, _ = w.Write(data)
} }
}() })
} }
wg.Wait() wg.Wait()

View File

@ -65,10 +65,7 @@ func (w *RateLimitedWriter) Write(p []byte) (int, error) {
w.bucket = w.rate // 简化:每秒补充 rate 个令牌 w.bucket = w.rate // 简化:每秒补充 rate 个令牌
} }
chunk := int64(n - written) chunk := min(int64(n-written), w.bucket)
if chunk > w.bucket {
chunk = w.bucket
}
nw, err := w.writer.Write(p[written : written+int(chunk)]) nw, err := w.writer.Write(p[written : written+int(chunk)])
written += nw written += nw

View File

@ -15,6 +15,8 @@
// 作者xfy // 作者xfy
package middleware package middleware
import "slices"
import "github.com/valyala/fasthttp" import "github.com/valyala/fasthttp"
// Middleware 中间件接口,定义中间件的标准方法。 // Middleware 中间件接口,定义中间件的标准方法。
@ -72,8 +74,8 @@ func NewChain(middlewares ...Middleware) *Chain {
// A -> B -> C -> H -> C -> B -> A // A -> B -> C -> H -> C -> B -> A
func (c *Chain) Apply(final fasthttp.RequestHandler) fasthttp.RequestHandler { func (c *Chain) Apply(final fasthttp.RequestHandler) fasthttp.RequestHandler {
handler := final handler := final
for i := len(c.middlewares) - 1; i >= 0; i-- { for _, v := range slices.Backward(c.middlewares) {
handler = c.middlewares[i].Process(handler) handler = v.Process(handler)
} }
return handler return handler
} }

View File

@ -27,6 +27,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"net" "net"
"slices"
"strings" "strings"
"sync" "sync"
"time" "time"
@ -218,13 +219,11 @@ func (ac *AccessControl) Check(ip net.IP) bool {
return false return false
} }
for _, c := range ac.geoipConfig.DenyCountries { if slices.Contains(ac.geoipConfig.DenyCountries, country) {
if country == c {
return false return false
} }
} }
} }
}
checkAllow: checkAllow:
// 3. 检查 CIDR 允许列表 // 3. 检查 CIDR 允许列表
@ -238,13 +237,11 @@ checkAllow:
if ac.geoip != nil && ac.geoipConfig.Enabled { if ac.geoip != nil && ac.geoipConfig.Enabled {
country, err := ac.geoip.LookupCountry(ip) country, err := ac.geoip.LookupCountry(ip)
if err == nil && country != geoPrivateDeny { if err == nil && country != geoPrivateDeny {
for _, c := range ac.geoipConfig.AllowCountries { if slices.Contains(ac.geoipConfig.AllowCountries, country) {
if country == c {
return true return true
} }
} }
} }
}
// 5. 返回默认操作 // 5. 返回默认操作
return ac.defaultAction == ActionAllow return ac.defaultAction == ActionAllow
@ -415,8 +412,8 @@ func (ac *AccessControl) getClientIP(ctx *fasthttp.RequestCtx) net.IP {
// 使用右侧(最接近客户端)的非可信 IP // 使用右侧(最接近客户端)的非可信 IP
if xff := ctx.Request.Header.Peek("X-Forwarded-For"); len(xff) > 0 { if xff := ctx.Request.Header.Peek("X-Forwarded-For"); len(xff) > 0 {
ips := strings.Split(string(xff), ",") ips := strings.Split(string(xff), ",")
for i := len(ips) - 1; i >= 0; i-- { for _, v := range slices.Backward(ips) {
ipStr := strings.TrimSpace(ips[i]) ipStr := strings.TrimSpace(v)
if ip := net.ParseIP(ipStr); ip != nil { if ip := net.ParseIP(ipStr); ip != nil {
// 检查该 IP 是否在可信代理列表中 // 检查该 IP 是否在可信代理列表中
trusted := false trusted := false

View File

@ -296,8 +296,8 @@ func parseArgon2idHash(hash string) (argon2Params, []byte, []byte, error) {
paramsStr := parts[3] paramsStr := parts[3]
params := defaultArgon2Params params := defaultArgon2Params
paramParts := strings.Split(paramsStr, ",") paramParts := strings.SplitSeq(paramsStr, ",")
for _, p := range paramParts { for p := range paramParts {
kv := strings.Split(p, "=") kv := strings.Split(p, "=")
if len(kv) != 2 { if len(kv) != 2 {
continue continue
@ -362,13 +362,13 @@ func (ba *BasicAuth) extractCredentials(ctx *fasthttp.RequestCtx) (string, strin
// 分割用户名:密码 // 分割用户名:密码
credentials := string(decoded) credentials := string(decoded)
idx := strings.Index(credentials, ":") before, after, ok := strings.Cut(credentials, ":")
if idx == -1 { if !ok {
return "", "", false return "", "", false
} }
username := credentials[:idx] username := before
password := credentials[idx+1:] password := after
return username, password, true return username, password, true
} }

View File

@ -169,8 +169,8 @@ func parseAuthURL(url string) (string, bool, error) {
if strings.HasPrefix(url, "https://") { if strings.HasPrefix(url, "https://") {
isTLS = true isTLS = true
url = strings.TrimPrefix(url, "https://") url = strings.TrimPrefix(url, "https://")
} else if strings.HasPrefix(url, "http://") { } else if after, ok := strings.CutPrefix(url, "http://"); ok {
url = strings.TrimPrefix(url, "http://") url = after
} }
// 提取主机部分 // 提取主机部分

View File

@ -329,7 +329,7 @@ func TestGeoIPLookup_ConcurrentAccess(t *testing.T) {
geoip := setupTestGeoIP(t, "allow") geoip := setupTestGeoIP(t, "allow")
done := make(chan bool, 10) done := make(chan bool, 10)
for i := 0; i < 10; i++ { for range 10 {
go func() { go func() {
_, err := geoip.LookupCountry(net.ParseIP("2.125.160.216")) _, err := geoip.LookupCountry(net.ParseIP("2.125.160.216"))
assert.NoError(t, err) assert.NoError(t, err)
@ -337,7 +337,7 @@ func TestGeoIPLookup_ConcurrentAccess(t *testing.T) {
}() }()
} }
for i := 0; i < 10; i++ { for range 10 {
<-done <-done
} }
} }

View File

@ -142,7 +142,7 @@ func newTokenBucketLimiter(cfg *config.RateLimitConfig) (*RateLimiter, error) {
} }
// 初始化 16 个分段锁桶 // 初始化 16 个分段锁桶
for i := 0; i < 16; i++ { for i := range 16 {
rl.shards[i] = shardedBucket{ rl.shards[i] = shardedBucket{
buckets: make(map[string]*tokenBucket), buckets: make(map[string]*tokenBucket),
} }
@ -437,7 +437,7 @@ func (rl *RateLimiter) Reset(key string) {
// //
// 清空所有桶记录,所有客户端将重新开始计数。 // 清空所有桶记录,所有客户端将重新开始计数。
func (rl *RateLimiter) ResetAll() { func (rl *RateLimiter) ResetAll() {
for i := 0; i < 16; i++ { for i := range 16 {
rl.shards[i].mu.Lock() rl.shards[i].mu.Lock()
rl.shards[i].buckets = make(map[string]*tokenBucket) rl.shards[i].buckets = make(map[string]*tokenBucket)
rl.shards[i].mu.Unlock() rl.shards[i].mu.Unlock()
@ -453,7 +453,7 @@ func (rl *RateLimiter) ResetAll() {
// - maxAge: 未使用桶的最大保留时间 // - maxAge: 未使用桶的最大保留时间
func (rl *RateLimiter) Cleanup(maxAge time.Duration) { func (rl *RateLimiter) Cleanup(maxAge time.Duration) {
now := time.Now() now := time.Now()
for i := 0; i < 16; i++ { for i := range 16 {
shard := &rl.shards[i] shard := &rl.shards[i]
shard.mu.Lock() shard.mu.Lock()
for key, bucket := range shard.buckets { for key, bucket := range shard.buckets {
@ -526,7 +526,7 @@ type RateLimitStats struct {
// - RateLimitStats: 包含桶数量、速率和容量的统计对象 // - RateLimitStats: 包含桶数量、速率和容量的统计对象
func (rl *RateLimiter) GetStats() RateLimitStats { func (rl *RateLimiter) GetStats() RateLimitStats {
totalBuckets := 0 totalBuckets := 0
for i := 0; i < 16; i++ { for i := range 16 {
rl.shards[i].mu.RLock() rl.shards[i].mu.RLock()
totalBuckets += len(rl.shards[i].buckets) totalBuckets += len(rl.shards[i].buckets)
rl.shards[i].mu.RUnlock() rl.shards[i].mu.RUnlock()
@ -551,7 +551,7 @@ type ConnLimiter struct {
keyFunc KeyFunc keyFunc KeyFunc
counts map[string]int64 counts map[string]int64
max int max int
current int64 current atomic.Int64
mu sync.RWMutex mu sync.RWMutex
perKey bool perKey bool
} }
@ -601,9 +601,9 @@ func NewConnLimiter(maxConns int, perKey bool, keyType string) (*ConnLimiter, er
func (cl *ConnLimiter) Acquire(ctx *fasthttp.RequestCtx) bool { func (cl *ConnLimiter) Acquire(ctx *fasthttp.RequestCtx) bool {
if !cl.perKey { if !cl.perKey {
// 全局限制(原子递增后检查溢出,避免 TOCTOU 竞态) // 全局限制(原子递增后检查溢出,避免 TOCTOU 竞态)
current := atomic.AddInt64(&cl.current, 1) current := cl.current.Add(1)
if current > int64(cl.max) { if current > int64(cl.max) {
atomic.AddInt64(&cl.current, -1) cl.current.Add(-1)
return false return false
} }
return true return true
@ -632,7 +632,7 @@ func (cl *ConnLimiter) Acquire(ctx *fasthttp.RequestCtx) bool {
// - ctx: FastHTTP 请求上下文 // - ctx: FastHTTP 请求上下文
func (cl *ConnLimiter) Release(ctx *fasthttp.RequestCtx) { func (cl *ConnLimiter) Release(ctx *fasthttp.RequestCtx) {
if !cl.perKey { if !cl.perKey {
atomic.AddInt64(&cl.current, -1) cl.current.Add(-1)
return return
} }

View File

@ -128,7 +128,7 @@ func BenchmarkRateLimiterCleanup_1000Buckets(b *testing.B) {
defer rl.StopCleanup() defer rl.StopCleanup()
// 预创建 1000 个桶 // 预创建 1000 个桶
for i := 0; i < 1000; i++ { for i := range 1000 {
rl.Allow("192.168.0." + string(rune(i))) rl.Allow("192.168.0." + string(rune(i)))
} }
@ -136,7 +136,7 @@ func BenchmarkRateLimiterCleanup_1000Buckets(b *testing.B) {
for b.Loop() { for b.Loop() {
rl.Cleanup(0) // 清理所有桶 rl.Cleanup(0) // 清理所有桶
// 重新创建桶以保持测试一致性 // 重新创建桶以保持测试一致性
for j := 0; j < 1000; j++ { for j := range 1000 {
rl.Allow("192.168.0." + string(rune(j))) rl.Allow("192.168.0." + string(rune(j)))
} }
} }
@ -217,7 +217,7 @@ func BenchmarkRateLimiterStats(b *testing.B) {
defer rl.StopCleanup() defer rl.StopCleanup()
// 预创建一些桶 // 预创建一些桶
for i := 0; i < 100; i++ { for i := range 100 {
rl.Allow("192.168.0." + string(rune(i))) rl.Allow("192.168.0." + string(rune(i)))
} }

View File

@ -112,7 +112,7 @@ func TestRateLimiterAllow(t *testing.T) {
key := "test-key" key := "test-key"
// Should allow burst requests // Should allow burst requests
for i := 0; i < 10; i++ { for i := range 10 {
if !rl.Allow(key) { if !rl.Allow(key) {
t.Errorf("Expected request %d to be allowed", i+1) t.Errorf("Expected request %d to be allowed", i+1)
} }
@ -141,7 +141,7 @@ func TestRateLimiterTokenRefill(t *testing.T) {
key := "refill-test" key := "refill-test"
// Exhaust the burst // Exhaust the burst
for i := 0; i < 100; i++ { for range 100 {
rl.Allow(key) rl.Allow(key)
} }
@ -607,7 +607,7 @@ func TestRateLimiter_GetRetryAfter(t *testing.T) {
// 创建一个桶并消耗令牌 // 创建一个桶并消耗令牌
key := "test-key" key := "test-key"
for i := 0; i < 10; i++ { for range 10 {
rl.Allow(key) rl.Allow(key)
} }

View File

@ -94,7 +94,7 @@ func NewSlidingWindowLimiter(window time.Duration, limit int, precise bool) *Sli
precise: precise, precise: precise,
} }
// 初始化16个分段锁桶 // 初始化16个分段锁桶
for i := 0; i < 16; i++ { for i := range 16 {
s.buckets[i] = &limiterBucket{ s.buckets[i] = &limiterBucket{
counters: make(map[string]*windowCounter), counters: make(map[string]*windowCounter),
} }
@ -225,7 +225,7 @@ func (s *SlidingWindowLimiter) Reset(key string) {
// ResetAll 重置所有计数器。 // ResetAll 重置所有计数器。
func (s *SlidingWindowLimiter) ResetAll() { func (s *SlidingWindowLimiter) ResetAll() {
for i := 0; i < 16; i++ { for i := range 16 {
bucket := s.buckets[i] bucket := s.buckets[i]
bucket.mu.Lock() bucket.mu.Lock()
bucket.counters = make(map[string]*windowCounter) bucket.counters = make(map[string]*windowCounter)
@ -239,7 +239,7 @@ func (s *SlidingWindowLimiter) ResetAll() {
// - maxAge: 未使用计数器的最大保留时间 // - maxAge: 未使用计数器的最大保留时间
func (s *SlidingWindowLimiter) Cleanup(maxAge time.Duration) { func (s *SlidingWindowLimiter) Cleanup(maxAge time.Duration) {
now := time.Now() now := time.Now()
for i := 0; i < 16; i++ { for i := range 16 {
bucket := s.buckets[i] bucket := s.buckets[i]
bucket.mu.Lock() bucket.mu.Lock()
for key, counter := range bucket.counters { for key, counter := range bucket.counters {
@ -267,7 +267,7 @@ type SlidingWindowStats struct {
// GetStats 返回统计信息。 // GetStats 返回统计信息。
func (s *SlidingWindowLimiter) GetStats() SlidingWindowStats { func (s *SlidingWindowLimiter) GetStats() SlidingWindowStats {
totalKeys := 0 totalKeys := 0
for i := 0; i < 16; i++ { for i := range 16 {
bucket := s.buckets[i] bucket := s.buckets[i]
bucket.mu.RLock() bucket.mu.RLock()
totalKeys += len(bucket.counters) totalKeys += len(bucket.counters)

View File

@ -73,7 +73,7 @@ func BenchmarkSlidingWindowCleanup(b *testing.B) {
sw := NewSlidingWindowLimiter(time.Second, 1000, false) sw := NewSlidingWindowLimiter(time.Second, 1000, false)
// 预创建 100 个键 // 预创建 100 个键
for i := 0; i < 100; i++ { for i := range 100 {
sw.Allow("192.168.0." + string(rune(i))) sw.Allow("192.168.0." + string(rune(i)))
} }
@ -89,7 +89,7 @@ func BenchmarkSlidingWindowGetCount(b *testing.B) {
key := "192.168.1.100" key := "192.168.1.100"
// 预先添加一些请求 // 预先添加一些请求
for i := 0; i < 100; i++ { for range 100 {
sw.Allow(key) sw.Allow(key)
} }
@ -131,7 +131,7 @@ func BenchmarkSlidingWindowStats(b *testing.B) {
sw := NewSlidingWindowLimiter(time.Second, 10000, false) sw := NewSlidingWindowLimiter(time.Second, 10000, false)
// 预创建一些键 // 预创建一些键
for i := 0; i < 50; i++ { for i := range 50 {
sw.Allow("192.168.0." + string(rune(i))) sw.Allow("192.168.0." + string(rune(i)))
} }

View File

@ -42,7 +42,7 @@ func TestSlidingWindowLimiter_Allow(t *testing.T) {
t.Run("近似模式-允许请求", func(t *testing.T) { t.Run("近似模式-允许请求", func(t *testing.T) {
limiter := NewSlidingWindowLimiter(time.Second, 10, false) limiter := NewSlidingWindowLimiter(time.Second, 10, false)
for i := 0; i < 10; i++ { for i := range 10 {
if !limiter.Allow("test-key") { if !limiter.Allow("test-key") {
t.Errorf("请求 %d 应该被允许", i+1) t.Errorf("请求 %d 应该被允许", i+1)
} }
@ -53,7 +53,7 @@ func TestSlidingWindowLimiter_Allow(t *testing.T) {
limiter := NewSlidingWindowLimiter(time.Second, 5, false) limiter := NewSlidingWindowLimiter(time.Second, 5, false)
// 发送 5 个请求 // 发送 5 个请求
for i := 0; i < 5; i++ { for range 5 {
limiter.Allow("test-key") limiter.Allow("test-key")
} }
@ -67,7 +67,7 @@ func TestSlidingWindowLimiter_Allow(t *testing.T) {
t.Run("精确模式-允许请求", func(t *testing.T) { t.Run("精确模式-允许请求", func(t *testing.T) {
limiter := NewSlidingWindowLimiter(time.Second, 10, true) limiter := NewSlidingWindowLimiter(time.Second, 10, true)
for i := 0; i < 10; i++ { for i := range 10 {
if !limiter.Allow("test-key") { if !limiter.Allow("test-key") {
t.Errorf("请求 %d 应该被允许", i+1) t.Errorf("请求 %d 应该被允许", i+1)
} }
@ -78,7 +78,7 @@ func TestSlidingWindowLimiter_Allow(t *testing.T) {
limiter := NewSlidingWindowLimiter(time.Second, 3, true) limiter := NewSlidingWindowLimiter(time.Second, 3, true)
// 发送 3 个请求 // 发送 3 个请求
for i := 0; i < 3; i++ { for i := range 3 {
if !limiter.Allow("test-key") { if !limiter.Allow("test-key") {
t.Errorf("请求 %d 应该被允许", i+1) t.Errorf("请求 %d 应该被允许", i+1)
} }
@ -108,7 +108,7 @@ func TestSlidingWindowLimiter_Reset(t *testing.T) {
limiter := NewSlidingWindowLimiter(time.Second, 5, true) limiter := NewSlidingWindowLimiter(time.Second, 5, true)
// 发送一些请求 // 发送一些请求
for i := 0; i < 5; i++ { for range 5 {
limiter.Allow("test-key") limiter.Allow("test-key")
} }
@ -173,7 +173,7 @@ func TestSlidingWindowLimiter_GetCount(t *testing.T) {
limiter := NewSlidingWindowLimiter(time.Second, 10, true) limiter := NewSlidingWindowLimiter(time.Second, 10, true)
// 发送 3 个请求 // 发送 3 个请求
for i := 0; i < 3; i++ { for range 3 {
limiter.Allow("test-key") limiter.Allow("test-key")
} }

View File

@ -165,7 +165,7 @@ func TestConcurrentAccess(t *testing.T) {
// 并发调用 DetectContentType // 并发调用 DetectContentType
wg.Add(numGoroutines) wg.Add(numGoroutines)
for i := 0; i < numGoroutines; i++ { for range numGoroutines {
go func() { go func() {
defer wg.Done() defer wg.Done()
DetectContentType("test.html") DetectContentType("test.html")
@ -174,7 +174,7 @@ func TestConcurrentAccess(t *testing.T) {
// 并发调用 AddTypes // 并发调用 AddTypes
wg.Add(numGoroutines) wg.Add(numGoroutines)
for i := 0; i < numGoroutines; i++ { for i := range numGoroutines {
go func(i int) { go func(i int) {
defer wg.Done() defer wg.Done()
AddTypes(map[string]string{".test": "application/x-test"}) AddTypes(map[string]string{".test": "application/x-test"})
@ -183,7 +183,7 @@ func TestConcurrentAccess(t *testing.T) {
// 并发调用 SetDefaultType 和 GetDefaultType // 并发调用 SetDefaultType 和 GetDefaultType
wg.Add(numGoroutines) wg.Add(numGoroutines)
for i := 0; i < numGoroutines; i++ { for range numGoroutines {
go func() { go func() {
defer wg.Done() defer wg.Done()
SetDefaultType("text/plain") SetDefaultType("text/plain")

View File

@ -71,7 +71,7 @@ func BenchmarkConnectionPool_Normal(b *testing.B) {
} }
// 预热连接池 // 预热连接池
for i := 0; i < 10; i++ { for range 10 {
ctx := &fasthttp.RequestCtx{} ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI("/api/test") ctx.Request.SetRequestURI("/api/test")
ctx.Request.Header.SetMethod(fasthttp.MethodGet) ctx.Request.Header.SetMethod(fasthttp.MethodGet)
@ -156,7 +156,7 @@ func BenchmarkConnectionPool_SmallBody(b *testing.B) {
} }
// 预热 // 预热
for i := 0; i < 5; i++ { for range 5 {
ctx := &fasthttp.RequestCtx{} ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI("/api/test") ctx.Request.SetRequestURI("/api/test")
p.ServeHTTP(ctx) p.ServeHTTP(ctx)
@ -221,7 +221,7 @@ func BenchmarkConnectionPool_MultiTarget(b *testing.B) {
targets := make([]*loadbalance.Target, 3) targets := make([]*loadbalance.Target, 3)
cleanups := make([]func(), 3) cleanups := make([]func(), 3)
for i := 0; i < 3; i++ { for i := range 3 {
addr, cleanup := setupInmemoryBackend([]byte("backend" + strconv.Itoa(i))) addr, cleanup := setupInmemoryBackend([]byte("backend" + strconv.Itoa(i)))
cleanups[i] = cleanup cleanups[i] = cleanup
targets[i] = &loadbalance.Target{ targets[i] = &loadbalance.Target{

View File

@ -74,7 +74,7 @@ const (
// 注意:从 pool 获取的 map 使用后不能 Put 回 pool // 注意:从 pool 获取的 map 使用后不能 Put 回 pool
// 因为 cache.Set 存储了 map 引用。 // 因为 cache.Set 存储了 map 引用。
var headersPool = sync.Pool{ var headersPool = sync.Pool{
New: func() interface{} { New: func() any {
return make(map[string]string, 20) return make(map[string]string, 20)
}, },
} }

View File

@ -187,7 +187,7 @@ func BenchmarkProxyForwardMultipleTargets(b *testing.B) {
targets := make([]*loadbalance.Target, numTargets) targets := make([]*loadbalance.Target, numTargets)
cleanups := make([]func(), numTargets) cleanups := make([]func(), numTargets)
for i := 0; i < numTargets; i++ { for i := range numTargets {
addr, cleanup := setupMockBackend(smallBody) addr, cleanup := setupMockBackend(smallBody)
cleanups[i] = cleanup cleanups[i] = cleanup
targets[i] = &loadbalance.Target{ targets[i] = &loadbalance.Target{

View File

@ -110,10 +110,7 @@ func (p *Proxy) startDNSRefreshLoop() {
} }
// 刷新间隔为 TTL / 2 // 刷新间隔为 TTL / 2
interval := ttl / 2 interval := max(ttl/2, time.Second)
if interval < time.Second {
interval = time.Second
}
ticker := time.NewTicker(interval) ticker := time.NewTicker(interval)
defer ticker.Stop() defer ticker.Stop()

View File

@ -39,7 +39,7 @@ import (
// wsBufPool WebSocket 数据转发 buffer pool。 // wsBufPool WebSocket 数据转发 buffer pool。
// 复用 32KB buffer 避免每次 copyData 调用分配。 // 复用 32KB buffer 避免每次 copyData 调用分配。
var wsBufPool = sync.Pool{ var wsBufPool = sync.Pool{
New: func() interface{} { New: func() any {
buf := make([]byte, 32*1024) buf := make([]byte, 32*1024)
return &buf return &buf
}, },

View File

@ -682,7 +682,7 @@ func TestWebSocketBridge_Concurrent(t *testing.T) {
var wg sync.WaitGroup var wg sync.WaitGroup
errCh := make(chan error, numBridges) errCh := make(chan error, numBridges)
for i := 0; i < numBridges; i++ { for i := range numBridges {
wg.Add(1) wg.Add(1)
go func(id int) { go func(id int) {
defer wg.Done() defer wg.Done()

View File

@ -395,14 +395,12 @@ func TestMockDNSConcurrentAccess(t *testing.T) {
concurrency := 10 concurrency := 10
iterations := 100 iterations := 100
for i := 0; i < concurrency; i++ { for range concurrency {
wg.Add(1) wg.Go(func() {
go func() { for range iterations {
defer wg.Done()
for j := 0; j < iterations; j++ {
_, _ = resolver.LookupHostWithCache(context.Background(), "localhost") _, _ = resolver.LookupHostWithCache(context.Background(), "localhost")
} }
}() })
} }
wg.Wait() wg.Wait()
@ -492,16 +490,14 @@ func TestMockDNSCacheEntryConcurrency(t *testing.T) {
var readCount atomic.Int64 var readCount atomic.Int64
// 并发读取 // 并发读取
for i := 0; i < 100; i++ { for range 100 {
wg.Add(1) wg.Go(func() {
go func() {
defer wg.Done()
entry.mu.RLock() entry.mu.RLock()
_ = entry.IPs _ = entry.IPs
_ = entry.ExpiresAt _ = entry.ExpiresAt
entry.mu.RUnlock() entry.mu.RUnlock()
readCount.Add(1) readCount.Add(1)
}() })
} }
wg.Wait() wg.Wait()

View File

@ -373,10 +373,7 @@ func (r *DNSResolver) Start() error {
// refreshLoop 后台刷新循环。 // refreshLoop 后台刷新循环。
func (r *DNSResolver) refreshLoop() { func (r *DNSResolver) refreshLoop() {
// 刷新间隔为 TTL / 2 // 刷新间隔为 TTL / 2
interval := r.config.TTL() / 2 interval := max(r.config.TTL()/2, time.Second)
if interval < time.Second {
interval = time.Second
}
ticker := time.NewTicker(interval) ticker := time.NewTicker(interval)
defer ticker.Stop() defer ticker.Stop()

View File

@ -27,7 +27,7 @@ func createTestResolver() *DNSResolver {
r := New(cfg).(*DNSResolver) r := New(cfg).(*DNSResolver)
// 预填充缓存条目,模拟真实的解析场景 // 预填充缓存条目,模拟真实的解析场景
for i := 0; i < 100; i++ { for i := range 100 {
host := fmt.Sprintf("host%d.example.com", i) host := fmt.Sprintf("host%d.example.com", i)
r.storeCache(host, &DNSCacheEntry{ r.storeCache(host, &DNSCacheEntry{
IPs: []string{fmt.Sprintf("192.168.1.%d", i%256), fmt.Sprintf("192.168.2.%d", i%256)}, IPs: []string{fmt.Sprintf("192.168.1.%d", i%256), fmt.Sprintf("192.168.2.%d", i%256)},
@ -214,7 +214,7 @@ func BenchmarkDNSResolverMixedWorkload(b *testing.B) {
ctx := context.Background() ctx := context.Background()
// 预填充一些缓存 // 预填充一些缓存
for i := 0; i < 50; i++ { for i := range 50 {
host := fmt.Sprintf("cached%d.example.com", i) host := fmt.Sprintf("cached%d.example.com", i)
r.storeCache(host, &DNSCacheEntry{ r.storeCache(host, &DNSCacheEntry{
IPs: []string{fmt.Sprintf("192.168.1.%d", i%256)}, IPs: []string{fmt.Sprintf("192.168.1.%d", i%256)},

View File

@ -203,7 +203,7 @@ func TestCacheHitRate(t *testing.T) {
r.mu.Unlock() r.mu.Unlock()
// 3 次命中 // 3 次命中
for i := 0; i < 3; i++ { for range 3 {
_, _ = r.LookupHostWithCache(context.Background(), "test.example.com") _, _ = r.LookupHostWithCache(context.Background(), "test.example.com")
} }
@ -356,7 +356,7 @@ func TestClearCache(t *testing.T) {
r := New(cfg).(*DNSResolver) r := New(cfg).(*DNSResolver)
// 添加多个缓存 // 添加多个缓存
for i := 0; i < 5; i++ { for i := range 5 {
host := fmt.Sprintf("test%d.example.com", i) host := fmt.Sprintf("test%d.example.com", i)
r.storeCache(host, &DNSCacheEntry{ r.storeCache(host, &DNSCacheEntry{
IPs: []string{fmt.Sprintf("192.168.1.%d", i)}, IPs: []string{fmt.Sprintf("192.168.1.%d", i)},
@ -397,12 +397,10 @@ func TestConcurrentAccess(t *testing.T) {
// 并发读取 // 并发读取
var wg sync.WaitGroup var wg sync.WaitGroup
for i := 0; i < 100; i++ { for range 100 {
wg.Add(1) wg.Go(func() {
go func() {
defer wg.Done()
_, _ = r.LookupHostWithCache(context.Background(), "test.example.com") _, _ = r.LookupHostWithCache(context.Background(), "test.example.com")
}() })
} }
wg.Wait() wg.Wait()
@ -578,7 +576,7 @@ func TestCacheSizeLimit(t *testing.T) {
r := New(cfg).(*DNSResolver) r := New(cfg).(*DNSResolver)
// 添加 5 个缓存条目,应淘汰 2 个 // 添加 5 个缓存条目,应淘汰 2 个
for i := 0; i < 5; i++ { for i := range 5 {
host := fmt.Sprintf("host%d.example.com", i) host := fmt.Sprintf("host%d.example.com", i)
r.storeCache(host, &DNSCacheEntry{ r.storeCache(host, &DNSCacheEntry{
IPs: []string{fmt.Sprintf("192.168.1.%d", i)}, IPs: []string{fmt.Sprintf("192.168.1.%d", i)},
@ -623,7 +621,7 @@ func TestCacheSizeZero(t *testing.T) {
r := New(cfg).(*DNSResolver) r := New(cfg).(*DNSResolver)
// 添加大量缓存条目 // 添加大量缓存条目
for i := 0; i < 100; i++ { for i := range 100 {
host := fmt.Sprintf("host%d.example.com", i) host := fmt.Sprintf("host%d.example.com", i)
r.storeCache(host, &DNSCacheEntry{ r.storeCache(host, &DNSCacheEntry{
IPs: []string{fmt.Sprintf("192.168.1.%d", i%256)}, IPs: []string{fmt.Sprintf("192.168.1.%d", i%256)},

View File

@ -195,10 +195,8 @@ func (p *GoroutinePool) Submit(ctx *fasthttp.RequestCtx, task Task) error {
// worker 从任务队列获取任务执行,空闲超时后自动退出(保持最小数量)。 // worker 从任务队列获取任务执行,空闲超时后自动退出(保持最小数量)。
func (p *GoroutinePool) startWorker() { func (p *GoroutinePool) startWorker() {
atomic.AddInt32(&p.workers, 1) atomic.AddInt32(&p.workers, 1)
p.wg.Add(1)
go func() { p.wg.Go(func() {
defer p.wg.Done()
defer atomic.AddInt32(&p.workers, -1) defer atomic.AddInt32(&p.workers, -1)
idleTimer := time.NewTimer(p.idleTimeout) idleTimer := time.NewTimer(p.idleTimeout)
@ -233,7 +231,7 @@ func (p *GoroutinePool) startWorker() {
return return
} }
} }
}() })
} }
// Stats 返回池的统计信息。 // Stats 返回池的统计信息。

View File

@ -57,7 +57,7 @@ func BenchmarkGoroutinePoolParallel(b *testing.B) {
task := func(_ *fasthttp.RequestCtx) { task := func(_ *fasthttp.RequestCtx) {
// 模拟微小工作负载 // 模拟微小工作负载
sum := 0 sum := 0
for j := 0; j < 100; j++ { for j := range 100 {
sum += j sum += j
} }
_ = sum _ = sum
@ -91,7 +91,7 @@ func BenchmarkGoroutinePoolSubmit_BlockingPath(b *testing.B) {
} }
// 提交任务使队列保持满状态 // 提交任务使队列保持满状态
for i := 0; i < 5; i++ { for range 5 {
go func() { go func() {
for { for {
_ = pool.Submit(ctx, slowTask) _ = pool.Submit(ctx, slowTask)
@ -136,7 +136,7 @@ func BenchmarkGoroutinePoolQueueFull(b *testing.B) {
task := func(_ *fasthttp.RequestCtx) { task := func(_ *fasthttp.RequestCtx) {
// 模拟微小工作负载 // 模拟微小工作负载
sum := 0 sum := 0
for j := 0; j < 10; j++ { for j := range 10 {
sum += j sum += j
} }
_ = sum _ = sum
@ -167,7 +167,7 @@ func BenchmarkGoroutinePoolWorkerRecycle(b *testing.B) {
time.Sleep(100 * time.Microsecond) time.Sleep(100 * time.Microsecond)
} }
for j := 0; j < 30; j++ { for range 30 {
go pool.Submit(ctx, task) go pool.Submit(ctx, task)
} }
@ -201,7 +201,7 @@ func BenchmarkGoroutinePoolSubmitWithWork(b *testing.B) {
task := func(_ *fasthttp.RequestCtx) { task := func(_ *fasthttp.RequestCtx) {
// 模拟中等计算量 // 模拟中等计算量
sum := 0 sum := 0
for i := 0; i < 1000; i++ { for i := range 1000 {
sum += i sum += i
} }
_ = sum _ = sum

View File

@ -138,15 +138,13 @@ func TestPoolConcurrentSubmit(t *testing.T) {
var counter atomic.Int32 var counter atomic.Int32
var wg sync.WaitGroup var wg sync.WaitGroup
for i := 0; i < 100; i++ { for range 100 {
wg.Add(1) wg.Go(func() {
go func() {
defer wg.Done()
_ = p.Submit(nil, func(_ *fasthttp.RequestCtx) { _ = p.Submit(nil, func(_ *fasthttp.RequestCtx) {
counter.Add(1) counter.Add(1)
}) })
}() })
} }
wg.Wait() wg.Wait()
@ -395,7 +393,7 @@ func TestPoolSubmit_MultipleQueuedTasks(t *testing.T) {
var counter atomic.Int32 var counter atomic.Int32
// 快速提交多个任务 // 快速提交多个任务
for i := 0; i < 5; i++ { for range 5 {
_ = p.Submit(nil, func(*fasthttp.RequestCtx) { _ = p.Submit(nil, func(*fasthttp.RequestCtx) {
counter.Add(1) counter.Add(1)
}) })

View File

@ -122,12 +122,10 @@ func TestWriteGoroutineProfile(t *testing.T) {
// 启动一些 goroutine // 启动一些 goroutine
var wg sync.WaitGroup var wg sync.WaitGroup
for i := 0; i < 5; i++ { for range 5 {
wg.Add(1) wg.Go(func() {
go func() {
defer wg.Done()
select {} select {}
}() })
} }
writeGoroutineProfile(&buf) writeGoroutineProfile(&buf)
@ -242,7 +240,7 @@ func TestCPUProfileMutex(t *testing.T) {
var buf bytes.Buffer var buf bytes.Buffer
// 启动多个 goroutine 同时操作 // 启动多个 goroutine 同时操作
for i := 0; i < 10; i++ { for range 10 {
wg.Add(2) wg.Add(2)
go func() { go func() {
defer wg.Done() defer wg.Done()

View File

@ -109,7 +109,7 @@ func TestStopAfterStop(t *testing.T) {
s := New(cfg) s := New(cfg)
// 多次调用 StopWithTimeout 应该都是安全的 // 多次调用 StopWithTimeout 应该都是安全的
for i := 0; i < 3; i++ { for i := range 3 {
err := s.StopWithTimeout(5 * time.Second) err := s.StopWithTimeout(5 * time.Second)
if err != nil { if err != nil {
t.Errorf("StopWithTimeout() call %d returned error: %v", i+1, err) t.Errorf("StopWithTimeout() call %d returned error: %v", i+1, err)
@ -375,7 +375,7 @@ func TestTrackStats_MultipleRequests(t *testing.T) {
wrappedHandler := s.trackStats(handler) wrappedHandler := s.trackStats(handler)
// 执行多次请求 // 执行多次请求
for i := 0; i < 10; i++ { for range 10 {
ctx := &fasthttp.RequestCtx{} ctx := &fasthttp.RequestCtx{}
ctx.Init(&fasthttp.Request{}, nil, nil) ctx.Init(&fasthttp.Request{}, nil, nil)
wrappedHandler(ctx) wrappedHandler(ctx)
@ -1508,7 +1508,7 @@ func TestServer_GetProxyCacheStats_AllProxiesWithCache(t *testing.T) {
// 创建多个带缓存的代理 // 创建多个带缓存的代理
proxies := make([]*proxy.Proxy, 3) proxies := make([]*proxy.Proxy, 3)
for i := 0; i < 3; i++ { for i := range 3 {
proxyCfg := &config.ProxyConfig{ proxyCfg := &config.ProxyConfig{
Path: fmt.Sprintf("/api%d", i), Path: fmt.Sprintf("/api%d", i),
LoadBalance: "round_robin", LoadBalance: "round_robin",
@ -1552,7 +1552,7 @@ func TestServer_GetProxyCacheStats_AllProxiesNoCache(t *testing.T) {
// 创建多个不带缓存的代理 // 创建多个不带缓存的代理
proxies := make([]*proxy.Proxy, 3) proxies := make([]*proxy.Proxy, 3)
for i := 0; i < 3; i++ { for i := range 3 {
proxyCfg := &config.ProxyConfig{ proxyCfg := &config.ProxyConfig{
Path: fmt.Sprintf("/api%d", i), Path: fmt.Sprintf("/api%d", i),
LoadBalance: "round_robin", LoadBalance: "round_robin",
@ -2731,7 +2731,7 @@ func TestShutdownServers_RunningServers(t *testing.T) {
servers := make([]*fasthttp.Server, 2) servers := make([]*fasthttp.Server, 2)
listeners := make([]net.Listener, 2) listeners := make([]net.Listener, 2)
for i := 0; i < 2; i++ { for i := range 2 {
ln, err := net.Listen("tcp", "127.0.0.1:0") ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil { if err != nil {
t.Fatalf("failed to create listener: %v", err) t.Fatalf("failed to create listener: %v", err)
@ -2773,7 +2773,7 @@ func TestShutdownServers_ManyServers(t *testing.T) {
// 创建大量服务器 // 创建大量服务器
count := 50 count := 50
servers := make([]*fasthttp.Server, count) servers := make([]*fasthttp.Server, count)
for i := 0; i < count; i++ { for i := range count {
servers[i] = &fasthttp.Server{ servers[i] = &fasthttp.Server{
Handler: func(ctx *fasthttp.RequestCtx) { ctx.SetBodyString("test") }, Handler: func(ctx *fasthttp.RequestCtx) { ctx.SetBodyString("test") },
} }
@ -2792,7 +2792,7 @@ func TestShutdownServers_MixedNilAndRealServers(t *testing.T) {
count := 20 count := 20
servers := make([]*fasthttp.Server, count) servers := make([]*fasthttp.Server, count)
for i := 0; i < count; i++ { for i := range count {
if i%2 == 0 { if i%2 == 0 {
servers[i] = nil servers[i] = nil
} else { } else {
@ -2814,16 +2814,14 @@ func TestShutdownServers_ConcurrentSafety(t *testing.T) {
// 并发调用 shutdownServers // 并发调用 shutdownServers
var wg sync.WaitGroup var wg sync.WaitGroup
for i := 0; i < 10; i++ { for range 10 {
wg.Add(1) wg.Go(func() {
go func() {
defer wg.Done()
servers := []*fasthttp.Server{ servers := []*fasthttp.Server{
{Handler: func(ctx *fasthttp.RequestCtx) { ctx.SetBodyString("test") }}, {Handler: func(ctx *fasthttp.RequestCtx) { ctx.SetBodyString("test") }},
{Handler: func(ctx *fasthttp.RequestCtx) { ctx.SetBodyString("test") }}, {Handler: func(ctx *fasthttp.RequestCtx) { ctx.SetBodyString("test") }},
} }
_ = shutdownServers(ctx, servers) _ = shutdownServers(ctx, servers)
}() })
} }
wg.Wait() wg.Wait()
} }

View File

@ -94,7 +94,7 @@ func (u *UpgradeManager) WritePid() error {
} }
pid := os.Getpid() pid := os.Getpid()
return os.WriteFile(u.pidFile, []byte(fmt.Sprintf("%d", pid)), 0o644) return os.WriteFile(u.pidFile, fmt.Appendf(nil, "%d", pid), 0o644)
} }
// ReadOldPid 读取旧进程 PID。 // ReadOldPid 读取旧进程 PID。
@ -223,7 +223,7 @@ func (u *UpgradeManager) GracefulUpgrade(newBinary string) error {
// 写入新 PID 到文件 // 写入新 PID 到文件
if u.pidFile != "" { if u.pidFile != "" {
_ = os.WriteFile(u.pidFile, []byte(fmt.Sprintf("%d", newPid)), 0o644) _ = os.WriteFile(u.pidFile, fmt.Appendf(nil, "%d", newPid), 0o644)
} }
// 启动 goroutine 等待子进程结束,避免产生僵尸进程 // 启动 goroutine 等待子进程结束,避免产生僵尸进程

View File

@ -184,7 +184,7 @@ func TestSessionTicketManager_KeyRetention(t *testing.T) {
defer mgr.Stop() defer mgr.Stop()
// 生成多个密钥 // 生成多个密钥
for i := 0; i < 5; i++ { for i := range 5 {
if err := mgr.RotateKey(); err != nil { if err := mgr.RotateKey(); err != nil {
t.Fatalf("RotateKey() failed at iteration %d: %v", i, err) t.Fatalf("RotateKey() failed at iteration %d: %v", i, err)
} }
@ -382,7 +382,7 @@ func TestSessionTicketManager_ConcurrentAccess(t *testing.T) {
// 协程 1: 持续获取密钥 // 协程 1: 持续获取密钥
go func() { go func() {
for i := 0; i < 100; i++ { for range 100 {
_ = mgr.GetKeys() _ = mgr.GetKeys()
time.Sleep(time.Millisecond) time.Sleep(time.Millisecond)
} }
@ -391,7 +391,7 @@ func TestSessionTicketManager_ConcurrentAccess(t *testing.T) {
// 协程 2: 持续获取状态 // 协程 2: 持续获取状态
go func() { go func() {
for i := 0; i < 100; i++ { for range 100 {
_ = mgr.GetStatus() _ = mgr.GetStatus()
time.Sleep(time.Millisecond) time.Sleep(time.Millisecond)
} }
@ -400,7 +400,7 @@ func TestSessionTicketManager_ConcurrentAccess(t *testing.T) {
// 协程 3: 手动轮换 // 协程 3: 手动轮换
go func() { go func() {
for i := 0; i < 20; i++ { for range 20 {
_ = mgr.RotateKey() _ = mgr.RotateKey()
time.Sleep(5 * time.Millisecond) time.Sleep(5 * time.Millisecond)
} }
@ -408,7 +408,7 @@ func TestSessionTicketManager_ConcurrentAccess(t *testing.T) {
}() }()
// 等待所有协程完成 // 等待所有协程完成
for i := 0; i < 3; i++ { for range 3 {
<-done <-done
} }
@ -442,7 +442,7 @@ func BenchmarkSessionTicketManager_GetKeys(b *testing.B) {
defer mgr.Stop() defer mgr.Stop()
// 预生成多个密钥 // 预生成多个密钥
for i := 0; i < 2; i++ { for range 2 {
_ = mgr.RotateKey() _ = mgr.RotateKey()
} }

View File

@ -557,7 +557,7 @@ func matchMarker(data []byte, marker []byte) bool {
if len(data) < len(marker) { if len(data) < len(marker) {
return false return false
} }
for i := 0; i < len(marker); i++ { for i := range marker {
if data[i] != marker[i] { if data[i] != marker[i] {
return false return false
} }

View File

@ -368,7 +368,7 @@ func BenchmarkOCSPStapling_GetStatus(b *testing.B) {
defer ocspMgr.Stop() defer ocspMgr.Stop()
// 注册一些模拟状态 // 注册一些模拟状态
for i := 0; i < 10; i++ { for i := range 10 {
serial := string(rune('0' + i)) serial := string(rune('0' + i))
ocspMgr.mu.Lock() ocspMgr.mu.Lock()
ocspMgr.responses[serial] = &ocspResponse{ ocspMgr.responses[serial] = &ocspResponse{
@ -430,7 +430,7 @@ func BenchmarkSessionResumption(b *testing.B) {
defer sessionMgr.Stop() defer sessionMgr.Stop()
// 预热密钥轮换 // 预热密钥轮换
for i := 0; i < 2; i++ { for range 2 {
_ = sessionMgr.RotateKey() _ = sessionMgr.RotateKey()
} }
@ -566,7 +566,7 @@ func BenchmarkSessionTicketManager_ApplyToTLSConfig(b *testing.B) {
} }
defer sessionMgr.Stop() defer sessionMgr.Stop()
for i := 0; i < 2; i++ { for range 2 {
_ = sessionMgr.RotateKey() _ = sessionMgr.RotateKey()
} }

View File

@ -65,7 +65,7 @@ func generateMultipleCerts(t *testing.T, count int) []byte {
t.Helper() t.Helper()
var pemData []byte var pemData []byte
for i := 0; i < count; i++ { for i := range count {
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil { if err != nil {
t.Fatalf("Failed to generate private key: %v", err) t.Fatalf("Failed to generate private key: %v", err)

View File

@ -63,7 +63,7 @@ type Balancer interface {
// 使用原子计数器实现线程安全的轮询选择,每次选择后计数器递增, // 使用原子计数器实现线程安全的轮询选择,每次选择后计数器递增,
// 确保请求均匀分布到所有健康目标。 // 确保请求均匀分布到所有健康目标。
type roundRobin struct { type roundRobin struct {
counter uint64 counter atomic.Uint64
healthyPool sync.Pool healthyPool sync.Pool
} }
@ -94,7 +94,7 @@ func (r *roundRobin) Select(targets []*Target) *Target {
r.healthyPool.Put(healthyPtr) r.healthyPool.Put(healthyPtr)
return nil return nil
} }
idx := atomic.AddUint64(&r.counter, 1) - 1 idx := r.counter.Add(1) - 1
result := healthy[idx%uint64(len(healthy))] result := healthy[idx%uint64(len(healthy))]
r.healthyPool.Put(healthyPtr) r.healthyPool.Put(healthyPtr)
return result return result
@ -147,7 +147,7 @@ func (l *leastConn) Select(targets []*Target) *Target {
// 根据目标服务器的权重分配请求,权重高的目标获得更多请求。 // 根据目标服务器的权重分配请求,权重高的目标获得更多请求。
// 使用原子计数器确保线程安全,支持不同权重的目标混合使用。 // 使用原子计数器确保线程安全,支持不同权重的目标混合使用。
type weightedRoundRobin struct { type weightedRoundRobin struct {
counter uint64 counter atomic.Uint64
healthyPool sync.Pool healthyPool sync.Pool
} }
@ -194,7 +194,7 @@ func (w *weightedRoundRobin) Select(targets []*Target) *Target {
} }
// 使用原子计数器确定位置 // 使用原子计数器确定位置
idx := atomic.AddUint64(&w.counter, 1) - 1 idx := w.counter.Add(1) - 1
pos := int(idx % uint64(totalWeight)) pos := int(idx % uint64(totalWeight))
// 找到对应位置的目标 // 找到对应位置的目标

View File

@ -71,7 +71,7 @@ func BenchmarkStreamFilterHealthyPreallocated(b *testing.B) {
// 创建目标列表 // 创建目标列表
targets := make([]*Target, targetCount) targets := make([]*Target, targetCount)
for i := 0; i < targetCount; i++ { for i := range targetCount {
targets[i] = &Target{ targets[i] = &Target{
addr: fmt.Sprintf("backend%d:8080", i), addr: fmt.Sprintf("backend%d:8080", i),
weight: 1, weight: 1,
@ -119,7 +119,7 @@ func BenchmarkUDPSessionAllocations(b *testing.B) {
var pool *sync.Pool var pool *sync.Pool
if tc.poolSize > 0 { if tc.poolSize > 0 {
pool = &sync.Pool{ pool = &sync.Pool{
New: func() interface{} { New: func() any {
return make([]byte, tc.bufSize) return make([]byte, tc.bufSize)
}, },
} }
@ -168,7 +168,7 @@ func BenchmarkUDPSessionGetOrCreate(b *testing.B) {
// 预创建一些客户端地址 // 预创建一些客户端地址
clientAddrs := make([]*net.UDPAddr, 100) clientAddrs := make([]*net.UDPAddr, 100)
for i := 0; i < 100; i++ { for i := range 100 {
clientAddrs[i], _ = net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", 20000+i)) clientAddrs[i], _ = net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", 20000+i))
} }
@ -206,7 +206,7 @@ func BenchmarkUDPSessionGetOnly(b *testing.B) {
// 预创建会话 // 预创建会话
clientAddrs := make([]*net.UDPAddr, 100) clientAddrs := make([]*net.UDPAddr, 100)
for i := 0; i < 100; i++ { for i := range 100 {
clientAddrs[i], _ = net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", 30000+i)) clientAddrs[i], _ = net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", 30000+i))
// 手动创建会话 // 手动创建会话
targetAddr, _ := net.ResolveUDPAddr("udp", upstream.targets[0].addr) targetAddr, _ := net.ResolveUDPAddr("udp", upstream.targets[0].addr)

View File

@ -77,7 +77,7 @@ func TestWeightedRoundRobinZeroWeight(t *testing.T) {
wrr := newWeightedRoundRobin().(*weightedRoundRobin) wrr := newWeightedRoundRobin().(*weightedRoundRobin)
// 权重为 0 或负数应视为权重 1 // 权重为 0 或负数应视为权重 1
for i := 0; i < 4; i++ { for range 4 {
selected := wrr.Select(targets) selected := wrr.Select(targets)
if selected == nil { if selected == nil {
t.Error("Select() should return target with zero/negative weight") t.Error("Select() should return target with zero/negative weight")

View File

@ -77,7 +77,7 @@ func TestRoundRobinBalancer(t *testing.T) {
// 测试轮询 // 测试轮询
results := make(map[string]int) results := make(map[string]int)
for i := 0; i < 6; i++ { for range 6 {
selected := rr.Select(targets) selected := rr.Select(targets)
if selected == nil { if selected == nil {
t.Error("Expected non-nil target") t.Error("Expected non-nil target")
@ -147,7 +147,7 @@ func TestWeightedRoundRobinBalancer(t *testing.T) {
// 测试加权分布3:1 比例 // 测试加权分布3:1 比例
results := make(map[string]int) results := make(map[string]int)
for i := 0; i < 8; i++ { for range 8 {
selected := wrr.Select(targets) selected := wrr.Select(targets)
if selected == nil { if selected == nil {
t.Error("Expected non-nil target") t.Error("Expected non-nil target")
@ -306,12 +306,10 @@ func TestConcurrentConnections(t *testing.T) {
// 并发增加连接数 // 并发增加连接数
var wg sync.WaitGroup var wg sync.WaitGroup
for i := 0; i < 100; i++ { for range 100 {
wg.Add(1) wg.Go(func() {
go func() {
defer wg.Done()
atomic.AddInt64(&s.connCount, 1) atomic.AddInt64(&s.connCount, 1)
}() })
} }
wg.Wait() wg.Wait()
@ -498,7 +496,7 @@ func TestRoundRobinBalancerWithSingleTarget(t *testing.T) {
targets[0].healthy.Store(true) targets[0].healthy.Store(true)
// 测试单个健康目标 // 测试单个健康目标
for i := 0; i < 5; i++ { for range 5 {
target := rb.Select(targets) target := rb.Select(targets)
if target == nil { if target == nil {
t.Error("Expected non-nil target") t.Error("Expected non-nil target")

View File

@ -138,7 +138,7 @@ func TestVariableExpansionPerformance(t *testing.T) {
// 执行多次展开 // 执行多次展开
start := time.Now() start := time.Now()
iterations := 10000 iterations := 10000
for i := 0; i < iterations; i++ { for range iterations {
_ = vc.Expand(template) _ = vc.Expand(template)
} }
elapsed := time.Since(start) elapsed := time.Since(start)

View File

@ -353,7 +353,7 @@ func TestSetSSLClientInfoInContext_WithPeerCert(t *testing.T) {
Raw: make([]byte, 25), // 模拟原始数据25字节 Raw: make([]byte, 25), // 模拟原始数据25字节
} }
// 填充可预测的原始数据 // 填充可预测的原始数据
for i := 0; i < 25; i++ { for i := range 25 {
cert.Raw[i] = byte(i + 1) cert.Raw[i] = byte(i + 1)
} }
@ -367,7 +367,7 @@ func TestSetSSLClientInfoInContext_WithPeerCert(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
key string key string
expected interface{} expected any
}{ }{
{"verify", VarSSLClientVerify, "SUCCESS"}, {"verify", VarSSLClientVerify, "SUCCESS"},
{"peer_cert_present", "tls_peer_cert_present", true}, {"peer_cert_present", "tls_peer_cert_present", true},

View File

@ -53,7 +53,7 @@ type Context struct {
// pool 用于复用 Context // pool 用于复用 Context
var pool = sync.Pool{ var pool = sync.Pool{
New: func() interface{} { New: func() any {
return &Context{ return &Context{
store: make(map[string]string), store: make(map[string]string),
cache: make(map[string]string), cache: make(map[string]string),

View File

@ -9,6 +9,7 @@
package variable package variable
import ( import (
"slices"
"strings" "strings"
"testing" "testing"
@ -455,7 +456,7 @@ func TestPoolReuse(t *testing.T) {
ctx := mockRequestCtx(t) ctx := mockRequestCtx(t)
// 获取和释放多个 context确保没有 panic // 获取和释放多个 context确保没有 panic
for i := 0; i < 10; i++ { for i := range 10 {
vc := NewContext(ctx) vc := NewContext(ctx)
vc.Set("key", "value") vc.Set("key", "value")
if v, ok := vc.Get("key"); !ok || v != "value" { if v, ok := vc.Get("key"); !ok || v != "value" {
@ -813,12 +814,7 @@ func TestBuiltinVarNames(t *testing.T) {
// 检查是否包含一些已知变量 // 检查是否包含一些已知变量
hasVar := func(name string) bool { hasVar := func(name string) bool {
for _, n := range names { return slices.Contains(names, name)
if n == name {
return true
}
}
return false
} }
if !hasVar("host") { if !hasVar("host") {
@ -1109,9 +1105,9 @@ func TestGlobalVariablesConcurrent(_ *testing.T) {
done := make(chan bool) done := make(chan bool)
// 并发读取 // 并发读取
for i := 0; i < 10; i++ { for range 10 {
go func() { go func() {
for j := 0; j < 100; j++ { for range 100 {
_, _ = GetGlobalVariable("counter") _, _ = GetGlobalVariable("counter")
} }
done <- true done <- true
@ -1119,9 +1115,9 @@ func TestGlobalVariablesConcurrent(_ *testing.T) {
} }
// 并发写入 // 并发写入
for i := 0; i < 5; i++ { for range 5 {
go func() { go func() {
for j := 0; j < 50; j++ { for range 50 {
SetGlobalVariables(map[string]string{"counter": "updated"}) SetGlobalVariables(map[string]string{"counter": "updated"})
} }
done <- true done <- true
@ -1129,7 +1125,7 @@ func TestGlobalVariablesConcurrent(_ *testing.T) {
} }
// 等待所有 goroutine 完成 // 等待所有 goroutine 完成
for i := 0; i < 15; i++ { for range 15 {
<-done <-done
} }
} }