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 ./...; \
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
@echo "All checks passed."
@ -405,6 +425,8 @@ help:
@echo "Quality:"
@echo " make fmt - Format code"
@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 ""
@echo "Dependencies:"

View File

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

View File

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

View File

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

View File

@ -50,7 +50,7 @@ func createTestTargets(n int) ([]TestTarget, func()) {
targets := make([]TestTarget, n)
cleanups := make([]func(), n)
for i := 0; i < n; i++ {
for i := range n {
body := GenerateTestData(Size1KB)
addr, cleanup := SimpleMockBackend(200, body)
targets[i] = TestTarget{
@ -83,7 +83,7 @@ func CreateWeightedTestTargets(n int) ([]TestTarget, func()) {
targets := make([]TestTarget, n)
cleanups := make([]func(), n)
for i := 0; i < n; i++ {
for i := range n {
body := GenerateTestData(Size1KB)
addr, cleanup := SimpleMockBackend(200, body)
// Vary weights: 1, 2, 3, etc.
@ -111,7 +111,7 @@ func CreateDelayedTestTargets(n int, baseDelay time.Duration) ([]TestTarget, fun
targets := make([]TestTarget, n)
cleanups := make([]func(), n)
for i := 0; i < n; i++ {
for i := range n {
body := GenerateTestData(Size1KB)
// Each target has increasing delay
delay := baseDelay * time.Duration(i+1)
@ -140,7 +140,7 @@ func CreateErrorTestTargets(n int, baseErrorRate float64) ([]TestTarget, func())
targets := make([]TestTarget, n)
cleanups := make([]func(), n)
for i := 0; i < n; i++ {
for i := range n {
body := GenerateTestData(Size1KB)
// Vary error rates slightly per target
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)
// 预填充缓存
for i := 0; i < size; i++ {
for i := range size {
path := fmt.Sprintf("/file%d.txt", i)
data := []byte("cached data content")
_ = 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)
// 预填充到容量上限
for i := 0; i < size; i++ {
for i := range size {
path := fmt.Sprintf("/file%d.txt", i)
data := []byte("cached data content")
_ = 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)
// 预填充到容量上限,触发淘汰和 entry 复用
for i := 0; i < size; i++ {
for i := range size {
path := fmt.Sprintf("/file%d.txt", i)
data := []byte("cached data content")
_ = 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)
// 预填充缓存
for i := 0; i < size; i++ {
for i := range size {
path := fmt.Sprintf("/file%d.txt", i)
data := []byte("cached data content")
_ = 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)
// 预填充缓存
for i := 0; i < 1000; i++ {
for i := range 1000 {
path := fmt.Sprintf("/static/file%d.css", i)
data := make([]byte, 1024) // 1KB 数据
_ = fc.Set(path, data, int64(len(data)), time.Now())
@ -186,7 +186,7 @@ func BenchmarkFileCacheSizeEviction(b *testing.B) {
// 预填充到接近容量上限
data := make([]byte, 1024) // 1KB 每条
for i := 0; i < 1000; i++ {
for i := range 1000 {
path := fmt.Sprintf("/file%d.txt", i)
_ = 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)
// 预填充缓存
for i := 0; i < 100; i++ {
for i := range 100 {
path := fmt.Sprintf("/file%d.txt", i)
data := []byte("cached data")
_ = 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)
// 预填充缓存
for i := 0; i < 1000; i++ {
for i := range 1000 {
origKey := fmt.Sprintf("key%d", i)
hashKey := hashKeyBench(origKey)
data := []byte("response body")
@ -264,7 +264,7 @@ func BenchmarkProxyCacheConcurrent(b *testing.B) {
pc := NewProxyCache(nil, false, 0, 0, 0)
// 预填充缓存
for i := 0; i < 1000; i++ {
for i := range 1000 {
origKey := fmt.Sprintf("key%d", i)
hashKey := hashKeyBench(origKey)
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) {
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)
data := []byte("cached data")
_ = 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) {
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)
data := []byte("cached data")
_ = 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")
size := int64(len(data))
for i := 0; i < 1000; i++ {
for i := range 1000 {
path := fmt.Sprintf("/update/file%d.txt", i)
fc.Set(path, data, size, time.Now())
}
@ -78,7 +78,7 @@ func BenchmarkFileCacheSetAllocation_Eviction(b *testing.B) {
// 预填充到容量上限
data := []byte("test data content for benchmark")
size := int64(len(data))
for i := 0; i < 100; i++ {
for i := range 100 {
path := fmt.Sprintf("/evict/file%d.txt", i)
fc.Set(path, data, size, time.Now())
}
@ -103,7 +103,7 @@ func BenchmarkFileCacheSetAllocation_EvictionWithPool(b *testing.B) {
size := int64(len(data))
// 预填充
for i := 0; i < 100; i++ {
for i := range 100 {
path := fmt.Sprintf("/pool/file%d.txt", i)
fc.Set(path, data, size, time.Now())
}
@ -128,7 +128,7 @@ func BenchmarkFileCacheSetAllocation_MemoryLimit(b *testing.B) {
size := int64(len(data))
// 预填充到接近上限
for i := 0; i < 900; i++ {
for i := range 900 {
path := fmt.Sprintf("/mem/file%d.txt", i)
fc.Set(path, data, size, time.Now())
}
@ -172,7 +172,7 @@ func BenchmarkFileCacheSetAllocation_ConcurrentEviction(b *testing.B) {
size := int64(len(data))
// 预填充
for i := 0; i < 100; i++ {
for i := range 100 {
path := fmt.Sprintf("/concevict/file%d.txt", i)
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{})
}
@ -235,4 +235,4 @@ func BenchmarkFileCacheLRUList_PushFront(b *testing.B) {
lruList.Remove(old)
}
}
}
}

View File

@ -26,19 +26,19 @@ const shardCount = 16
// FileCacheShard 单个缓存分片。
type FileCacheShard struct {
entries map[string]*FileEntry
lruList *list.List
maxEntries int64
maxSize int64
inactive time.Duration
entries map[string]*FileEntry
lruList *list.List
maxEntries int64
maxSize int64
inactive time.Duration
currentSize int64
mu sync.RWMutex
entryPool sync.Pool
mu sync.RWMutex
entryPool sync.Pool
}
// ShardedFileCache 分片文件缓存。
type ShardedFileCache struct {
shards [shardCount]*FileCacheShard
shards [shardCount]*FileCacheShard
maxEntries int64
maxSize int64
inactive time.Duration
@ -61,7 +61,7 @@ func NewShardedFileCache(maxEntries, maxSize int64, inactive time.Duration) *Sha
perShardSize = maxSize
}
for i := 0; i < shardCount; i++ {
for i := range shardCount {
shard := &FileCacheShard{
maxEntries: perShardEntries,
maxSize: perShardSize,
@ -83,7 +83,7 @@ func NewShardedFileCache(maxEntries, maxSize int64, inactive time.Duration) *Sha
func (s *ShardedFileCache) getShard(path string) *FileCacheShard {
h := fnv.New64a()
h.Write([]byte(path))
return s.shards[h.Sum64() % shardCount]
return s.shards[h.Sum64()%shardCount]
}
// Get 获取缓存的文件。
@ -262,4 +262,4 @@ func (sh *FileCacheShard) evictLRU() {
return
}
sh.removeEntry(entry)
}
}

View File

@ -89,22 +89,22 @@ func TestGenerateConfigYAMLFieldsCoverage(t *testing.T) {
typ reflect.Type
name string
}{
{reflect.TypeOf(GeoIPConfig{}), "GeoIPConfig"},
{reflect.TypeOf(AuthRequestConfig{}), "AuthRequestConfig"},
{reflect.TypeOf(LuaGlobalSettings{}), "LuaGlobalSettings"},
{reflect.TypeOf(LimitRateConfig{}), "LimitRateConfig"},
{reflect.TypeOf(TypesConfig{}), "TypesConfig"},
{reflect.TypeFor[GeoIPConfig](), "GeoIPConfig"},
{reflect.TypeFor[AuthRequestConfig](), "AuthRequestConfig"},
{reflect.TypeFor[LuaGlobalSettings](), "LuaGlobalSettings"},
{reflect.TypeFor[LimitRateConfig](), "LimitRateConfig"},
{reflect.TypeFor[TypesConfig](), "TypesConfig"},
}
for _, c := range checks {
for i := 0; i < c.typ.NumField(); i++ {
tag := c.typ.Field(i).Tag.Get("yaml")
for field := range c.typ.Fields() {
tag := field.Tag.Get("yaml")
fieldName := strings.Split(tag, ",")[0]
if fieldName == "" || fieldName == "-" {
continue
}
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")
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 {
t.Fatalf("创建页面 %d 失败: %v", code, err)
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -339,7 +339,7 @@ func TestServer_MultipleStop(t *testing.T) {
server, _ := NewServer(cfg, handler, &tls.Config{})
// 多次调用 Stop 应该都是安全的
for i := 0; i < 3; i++ {
for i := range 3 {
err := server.Stop()
if err != nil {
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{})
// 多次调用 GracefulStop 应该都是安全的
for i := 0; i < 3; i++ {
for i := range 3 {
err := server.GracefulStop(1 * time.Second)
if err != nil {
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: 请求路径
// - count: 预热请求数量
func warmupProxy(p *proxy.Proxy, path string, count int) {
for i := 0; i < count; i++ {
for range count {
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI(path)
ctx.Request.Header.SetMethod(fasthttp.MethodGet)
@ -299,11 +299,11 @@ func BenchmarkE2EStaticFile(b *testing.B) {
b.ReportAllocs()
paths := []string{"/small.css", "/medium.json", "/assets/app.js", "/index.html"}
var counter uint64
var counter atomic.Uint64
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
idx := atomic.AddUint64(&counter, 1)
idx := counter.Add(1)
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI(paths[idx%uint64(len(paths))])
ctx.Request.Header.SetMethod(fasthttp.MethodGet)
@ -336,11 +336,11 @@ func BenchmarkE2EStaticFileCacheHit(b *testing.B) {
b.ReportAllocs()
paths := []string{"/small.css", "/medium.json", "/assets/app.js", "/index.html"}
var counter uint64
var counter atomic.Uint64
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
idx := atomic.AddUint64(&counter, 1)
idx := counter.Add(1)
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI(paths[idx%uint64(len(paths))])
ctx.Request.Header.SetMethod(fasthttp.MethodGet)
@ -568,7 +568,7 @@ ngx.header["X-Lua-Processed"] = "true"`
finalHandler := chain.Apply(wrappedByLua)
// 预热 Lua 引擎(字节码缓存)
for i := 0; i < 5; i++ {
for range 5 {
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI("/api/test")
ctx.Request.Header.SetMethod(fasthttp.MethodGet)
@ -638,7 +638,7 @@ func BenchmarkE2EMultiLuaPhase(b *testing.B) {
finalHandler := baseChain.Apply(wrappedByLua)
// 预热
for i := 0; i < 5; i++ {
for range 5 {
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI("/api/test")
ctx.Request.Header.SetMethod(fasthttp.MethodGet)
@ -884,7 +884,7 @@ func BenchmarkE2EMultipleRoutes(b *testing.B) {
b.ReportAllocs()
// 使用原子计数器轮询不同路径
var counter uint64
var counter atomic.Uint64
paths := []string{
"/api/v1/users",
"/api/v2/items",
@ -899,7 +899,7 @@ func BenchmarkE2EMultipleRoutes(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
idx := atomic.AddUint64(&counter, 1)
idx := counter.Add(1)
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI(paths[idx%uint64(len(paths))])
ctx.Request.Header.SetMethod(fasthttp.MethodGet)
@ -955,7 +955,7 @@ func BenchmarkE2EMultipleRoutesWithMiddleware(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()
var counter uint64
var counter atomic.Uint64
testPaths := []string{
"/api/users",
"/api/users/42",
@ -968,7 +968,7 @@ func BenchmarkE2EMultipleRoutesWithMiddleware(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
idx := atomic.AddUint64(&counter, 1)
idx := counter.Add(1)
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI(testPaths[idx%uint64(len(testPaths))])
ctx.Request.Header.SetMethod(fasthttp.MethodGet)
@ -1152,7 +1152,7 @@ func BenchmarkE2EInmemoryServer(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()
var counter uint64
var counter atomic.Uint64
paths := []string{"/api/test", "/static/small.css", "/static/medium.json", "/health", "/api/data"}
b.RunParallel(func(pb *testing.PB) {
@ -1162,7 +1162,7 @@ func BenchmarkE2EInmemoryServer(b *testing.B) {
defer fasthttp.ReleaseResponse(resp)
for pb.Next() {
idx := atomic.AddUint64(&counter, 1)
idx := counter.Add(1)
req.SetRequestURI("http://localhost" + paths[idx%uint64(len(paths))])
req.Header.SetMethod(fasthttp.MethodGet)
req.Header.Set("Host", "example.com")
@ -1316,11 +1316,11 @@ func BenchmarkE2ERewriteMiddleware(b *testing.B) {
"/old-api/settings",
"/v1/data/123",
}
var counter uint64
var counter atomic.Uint64
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
idx := atomic.AddUint64(&counter, 1)
idx := counter.Add(1)
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI(paths[idx%uint64(len(paths))])
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"}
deniedIPs := []string{"192.168.1.100", "172.16.0.1", "8.8.8.8"}
allIPs := append(allowedIPs, deniedIPs...)
var counter uint64
var counter atomic.Uint64
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
idx := atomic.AddUint64(&counter, 1)
idx := counter.Add(1)
clientIP := allIPs[idx%uint64(len(allIPs))]
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI("/api/test")
@ -1416,11 +1416,11 @@ func BenchmarkE2ERateLimiter(b *testing.B) {
b.ResetTimer()
b.ReportAllocs()
var counter uint64
var counter atomic.Uint64
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
idx := atomic.AddUint64(&counter, 1)
idx := counter.Add(1)
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI("/api/test")
ctx.Request.Header.SetMethod(fasthttp.MethodGet)
@ -1470,7 +1470,7 @@ func BenchmarkE2EBasicAuth(b *testing.B) {
b.ReportAllocs()
// 预热认证(缓存 bcrypt 结果)
for i := 0; i < 3; i++ {
for range 3 {
ctx := &fasthttp.RequestCtx{}
ctx.Request.SetRequestURI("/api/test")
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)
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)+`}`))
cleanups[i] = cleanup
targets[i] = &loadbalance.Target{
@ -215,8 +215,8 @@ func BenchmarkE2EFailover_DynamicToggle(b *testing.B) {
for pb.Next() {
// 每 100 次请求切换一个后端的健康状态
count := toggleCounter.Add(1)
if count % 100 == 0 {
targetIdx := int(count / 100) % len(targets)
if count%100 == 0 {
targetIdx := int(count/100) % len(targets)
current := targets[targetIdx].Healthy.Load()
targets[targetIdx].Healthy.Store(!current)
}
@ -271,7 +271,7 @@ func BenchmarkE2EFailover_AllUnhealthy(b *testing.B) {
// 验证负载均衡器选择逻辑的分配。
func BenchmarkE2EFailover_SelectOnly(b *testing.B) {
targets := make([]*loadbalance.Target, 5)
for i := 0; i < 5; i++ {
for i := range 5 {
targets[i] = &loadbalance.Target{
URL: "http://backend" + strconv.Itoa(i) + ":8080",
Weight: 1,
@ -287,4 +287,4 @@ func BenchmarkE2EFailover_SelectOnly(b *testing.B) {
for b.Loop() {
_ = rr.Select(targets)
}
}
}

View File

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

View File

@ -105,7 +105,7 @@ type Balancer interface {
// 并发安全counter 使用 atomic 操作,支持多 goroutine 并发调用。
type RoundRobin struct {
// counter 请求计数器,原子递增,用于确定轮询位置
counter uint64
counter atomic.Uint64
}
// 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))]
}
@ -139,7 +139,7 @@ func (r *RoundRobin) Select(targets []*Target) *Target {
// 并发安全counter 使用 atomic 操作,支持多 goroutine 并发调用。
type WeightedRoundRobin struct {
// counter 请求计数器,原子递增,用于确定权重分布位置
counter uint64
counter atomic.Uint64
}
// 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))
// 找到计算位置处的目标
@ -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))]
}
@ -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))
// 找到计算位置处的目标

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -87,10 +87,10 @@ type CodeCache struct {
ttl time.Duration
// 缓存命中次数
hits uint64
hits atomic.Uint64
// 缓存未命中次数
misses uint64
misses atomic.Uint64
// 读写锁
mu sync.RWMutex
@ -156,12 +156,12 @@ func (c *CodeCache) GetOrCompileInline(src string) (*glua.FunctionProto, error)
c.mu.RUnlock()
if ok && !c.isExpired(cached) {
atomic.AddUint64(&c.hits, 1)
c.hits.Add(1)
cached.AccessAt.Store(time.Now())
return cached.Proto, nil
}
atomic.AddUint64(&c.misses, 1)
c.misses.Add(1)
// 编译脚本
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) {
atomic.AddUint64(&c.hits, 1)
c.hits.Add(1)
cached.AccessAt.Store(time.Now())
return cached.Proto, nil
}
atomic.AddUint64(&c.misses, 1)
c.misses.Add(1)
// 读取文件
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) {
c.mu.RLock()
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 返回缓存命中率
func (c *CodeCache) HitRate() float64 {
hits := atomic.LoadUint64(&c.hits)
misses := atomic.LoadUint64(&c.misses)
hits := c.hits.Load()
misses := c.misses.Load()
total := hits + misses
if total == 0 {
return 0

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -73,7 +73,7 @@ func BenchmarkSlidingWindowCleanup(b *testing.B) {
sw := NewSlidingWindowLimiter(time.Second, 1000, false)
// 预创建 100 个键
for i := 0; i < 100; i++ {
for i := range 100 {
sw.Allow("192.168.0." + string(rune(i)))
}
@ -89,7 +89,7 @@ func BenchmarkSlidingWindowGetCount(b *testing.B) {
key := "192.168.1.100"
// 预先添加一些请求
for i := 0; i < 100; i++ {
for range 100 {
sw.Allow(key)
}
@ -131,7 +131,7 @@ func BenchmarkSlidingWindowStats(b *testing.B) {
sw := NewSlidingWindowLimiter(time.Second, 10000, false)
// 预创建一些键
for i := 0; i < 50; i++ {
for i := range 50 {
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) {
limiter := NewSlidingWindowLimiter(time.Second, 10, false)
for i := 0; i < 10; i++ {
for i := range 10 {
if !limiter.Allow("test-key") {
t.Errorf("请求 %d 应该被允许", i+1)
}
@ -53,7 +53,7 @@ func TestSlidingWindowLimiter_Allow(t *testing.T) {
limiter := NewSlidingWindowLimiter(time.Second, 5, false)
// 发送 5 个请求
for i := 0; i < 5; i++ {
for range 5 {
limiter.Allow("test-key")
}
@ -67,7 +67,7 @@ func TestSlidingWindowLimiter_Allow(t *testing.T) {
t.Run("精确模式-允许请求", func(t *testing.T) {
limiter := NewSlidingWindowLimiter(time.Second, 10, true)
for i := 0; i < 10; i++ {
for i := range 10 {
if !limiter.Allow("test-key") {
t.Errorf("请求 %d 应该被允许", i+1)
}
@ -78,7 +78,7 @@ func TestSlidingWindowLimiter_Allow(t *testing.T) {
limiter := NewSlidingWindowLimiter(time.Second, 3, true)
// 发送 3 个请求
for i := 0; i < 3; i++ {
for i := range 3 {
if !limiter.Allow("test-key") {
t.Errorf("请求 %d 应该被允许", i+1)
}
@ -108,7 +108,7 @@ func TestSlidingWindowLimiter_Reset(t *testing.T) {
limiter := NewSlidingWindowLimiter(time.Second, 5, true)
// 发送一些请求
for i := 0; i < 5; i++ {
for range 5 {
limiter.Allow("test-key")
}
@ -173,7 +173,7 @@ func TestSlidingWindowLimiter_GetCount(t *testing.T) {
limiter := NewSlidingWindowLimiter(time.Second, 10, true)
// 发送 3 个请求
for i := 0; i < 3; i++ {
for range 3 {
limiter.Allow("test-key")
}

View File

@ -165,7 +165,7 @@ func TestConcurrentAccess(t *testing.T) {
// 并发调用 DetectContentType
wg.Add(numGoroutines)
for i := 0; i < numGoroutines; i++ {
for range numGoroutines {
go func() {
defer wg.Done()
DetectContentType("test.html")
@ -174,7 +174,7 @@ func TestConcurrentAccess(t *testing.T) {
// 并发调用 AddTypes
wg.Add(numGoroutines)
for i := 0; i < numGoroutines; i++ {
for i := range numGoroutines {
go func(i int) {
defer wg.Done()
AddTypes(map[string]string{".test": "application/x-test"})
@ -183,7 +183,7 @@ func TestConcurrentAccess(t *testing.T) {
// 并发调用 SetDefaultType 和 GetDefaultType
wg.Add(numGoroutines)
for i := 0; i < numGoroutines; i++ {
for range numGoroutines {
go func() {
defer wg.Done()
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.Request.SetRequestURI("/api/test")
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.Request.SetRequestURI("/api/test")
p.ServeHTTP(ctx)
@ -221,7 +221,7 @@ func BenchmarkConnectionPool_MultiTarget(b *testing.B) {
targets := make([]*loadbalance.Target, 3)
cleanups := make([]func(), 3)
for i := 0; i < 3; i++ {
for i := range 3 {
addr, cleanup := setupInmemoryBackend([]byte("backend" + strconv.Itoa(i)))
cleanups[i] = cleanup
targets[i] = &loadbalance.Target{
@ -295,4 +295,4 @@ func BenchmarkHostClient_AcquireRelease(b *testing.B) {
fasthttp.ReleaseRequest(req)
fasthttp.ReleaseResponse(resp)
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -203,7 +203,7 @@ func TestCacheHitRate(t *testing.T) {
r.mu.Unlock()
// 3 次命中
for i := 0; i < 3; i++ {
for range 3 {
_, _ = r.LookupHostWithCache(context.Background(), "test.example.com")
}
@ -356,7 +356,7 @@ func TestClearCache(t *testing.T) {
r := New(cfg).(*DNSResolver)
// 添加多个缓存
for i := 0; i < 5; i++ {
for i := range 5 {
host := fmt.Sprintf("test%d.example.com", i)
r.storeCache(host, &DNSCacheEntry{
IPs: []string{fmt.Sprintf("192.168.1.%d", i)},
@ -397,12 +397,10 @@ func TestConcurrentAccess(t *testing.T) {
// 并发读取
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for range 100 {
wg.Go(func() {
_, _ = r.LookupHostWithCache(context.Background(), "test.example.com")
}()
})
}
wg.Wait()
@ -578,7 +576,7 @@ func TestCacheSizeLimit(t *testing.T) {
r := New(cfg).(*DNSResolver)
// 添加 5 个缓存条目,应淘汰 2 个
for i := 0; i < 5; i++ {
for i := range 5 {
host := fmt.Sprintf("host%d.example.com", i)
r.storeCache(host, &DNSCacheEntry{
IPs: []string{fmt.Sprintf("192.168.1.%d", i)},
@ -623,7 +621,7 @@ func TestCacheSizeZero(t *testing.T) {
r := New(cfg).(*DNSResolver)
// 添加大量缓存条目
for i := 0; i < 100; i++ {
for i := range 100 {
host := fmt.Sprintf("host%d.example.com", i)
r.storeCache(host, &DNSCacheEntry{
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 从任务队列获取任务执行,空闲超时后自动退出(保持最小数量)。
func (p *GoroutinePool) startWorker() {
atomic.AddInt32(&p.workers, 1)
p.wg.Add(1)
go func() {
defer p.wg.Done()
p.wg.Go(func() {
defer atomic.AddInt32(&p.workers, -1)
idleTimer := time.NewTimer(p.idleTimeout)
@ -233,7 +231,7 @@ func (p *GoroutinePool) startWorker() {
return
}
}
}()
})
}
// Stats 返回池的统计信息。

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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