fix: resolve golangci-lint issues across multiple packages

- stream: fix atomic.Int64 usage in tests and benchmarks
- server: fix errcheck, goconst ("tcp" -> constant), and govet shadow
- app: add missing ServerModeAuto case in requiresFullRestart
- lua: fix nolintlint unused directive warnings
- proxy: use `any` instead of `interface{}`
This commit is contained in:
xfy 2026-06-03 18:17:07 +08:00
parent 6f17bbad7e
commit 29752f62bd
8 changed files with 47 additions and 38 deletions

View File

@ -192,7 +192,7 @@ func (a *App) reloadConfig() {
for i, ln := range listeners { for i, ln := range listeners {
duped[i], err = server.DupListener(ln) duped[i], err = server.DupListener(ln)
if err != nil { if err != nil {
for j := 0; j < i; j++ { for j := range i {
_ = duped[j].Close() _ = duped[j].Close()
} }
a.logger.Error().Err(err).Msg("Failed to dup listener for reload") a.logger.Error().Err(err).Msg("Failed to dup listener for reload")
@ -289,6 +289,8 @@ func (a *App) requiresFullRestart(newCfg *config.Config) bool {
return true return true
} }
} }
case config.ServerModeAuto:
return true
} }
return false return false
} }

View File

@ -180,7 +180,7 @@ func RegisterLocationAPI(L *glua.LState, manager *LocationManager, ngx *glua.LTa
optionsTable := L.CheckTable(2) optionsTable := L.CheckTable(2)
optionsTable.ForEach(func(key, value glua.LValue) { optionsTable.ForEach(func(key, value glua.LValue) {
keyStr := glua.LVAsString(key) keyStr := glua.LVAsString(key)
//nolint:exhaustive // 只处理特定类型 //nolint:exhaustive,nolintlint
switch value.Type() { switch value.Type() {
case glua.LTString: case glua.LTString:
opts[keyStr] = glua.LVAsString(value) opts[keyStr] = glua.LVAsString(value)

View File

@ -308,7 +308,7 @@ func (api *ngxReqAPI) luaSetURIArgs(L *glua.LState) int {
// 获取参数类型 // 获取参数类型
argType := L.Get(1) argType := L.Get(1)
//nolint:exhaustive // 只处理特定类型 //nolint:exhaustive,nolintlint
switch argType.Type() { switch argType.Type() {
case glua.LTString: case glua.LTString:
// 如果是字符串,直接解析并设置 // 如果是字符串,直接解析并设置
@ -330,7 +330,7 @@ func (api *ngxReqAPI) luaSetURIArgs(L *glua.LState) int {
table.ForEach(func(key, value glua.LValue) { table.ForEach(func(key, value glua.LValue) {
keyStr := glua.LVAsString(key) keyStr := glua.LVAsString(key)
//nolint:exhaustive // 只处理特定类型 //nolint:exhaustive,nolintlint
switch value.Type() { switch value.Type() {
case glua.LTString: case glua.LTString:
// 类型断言检查 // 类型断言检查

View File

@ -57,7 +57,7 @@ import (
// proxyDebugLog 在 DEBUG 级别记录代理日志 // proxyDebugLog 在 DEBUG 级别记录代理日志
// 调用者必须先检查 logging.Debug().Enabled() 以避免不必要的内存分配 // 调用者必须先检查 logging.Debug().Enabled() 以避免不必要的内存分配
func proxyDebugLog(msg string, kv ...interface{}) { func proxyDebugLog(msg string, kv ...any) {
event := logging.Debug() event := logging.Debug()
for i := 0; i < len(kv)-1; i += 2 { for i := 0; i < len(kv)-1; i += 2 {
key, ok := kv[i].(string) key, ok := kv[i].(string)

View File

@ -46,6 +46,8 @@ import (
"rua.plus/lolly/internal/version" "rua.plus/lolly/internal/version"
) )
const networkTCP = "tcp"
// Server HTTP 服务器,封装 fasthttp.Server 并提供中间件链和生命周期管理。 // Server HTTP 服务器,封装 fasthttp.Server 并提供中间件链和生命周期管理。
// //
// 该结构体是服务器的核心实体,负责: // 该结构体是服务器的核心实体,负责:
@ -358,7 +360,7 @@ func (s *Server) createListener(cfg *config.ServerConfig) (net.Listener, error)
return listener, nil return listener, nil
} }
return net.Listen("tcp", listenAddr) return net.Listen(networkTCP, listenAddr)
} }
func (s *Server) matchInheritedListener(inherited []net.Listener, listenAddr string) net.Listener { func (s *Server) matchInheritedListener(inherited []net.Listener, listenAddr string) net.Listener {
@ -383,7 +385,7 @@ func (s *Server) matchInheritedListener(inherited []net.Listener, listenAddr str
if ln == nil { if ln == nil {
continue continue
} }
if ln.Addr().Network() != "tcp" { if ln.Addr().Network() != networkTCP {
continue continue
} }
if s.tcpAddrMatch(ln.Addr().String(), listenAddr) { if s.tcpAddrMatch(ln.Addr().String(), listenAddr) {
@ -426,14 +428,14 @@ func DupListener(ln net.Listener) (net.Listener, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("dup tcp listener: %w", err) return nil, fmt.Errorf("dup tcp listener: %w", err)
} }
defer file.Close() defer func() { _ = file.Close() }()
return net.FileListener(file) return net.FileListener(file)
case *net.UnixListener: case *net.UnixListener:
file, err := l.File() file, err := l.File()
if err != nil { if err != nil {
return nil, fmt.Errorf("dup unix listener: %w", err) return nil, fmt.Errorf("dup unix listener: %w", err)
} }
defer file.Close() defer func() { _ = file.Close() }()
return net.FileListener(file) return net.FileListener(file)
default: default:
return nil, fmt.Errorf("unsupported listener type: %T", ln) return nil, fmt.Errorf("unsupported listener type: %T", ln)
@ -467,8 +469,8 @@ func (s *Server) startSingleMode() error {
if err != nil { if err != nil {
logging.Error().Msg("Failed to create status handler: " + err.Error()) logging.Error().Msg("Failed to create status handler: " + err.Error())
} else { } else {
if err := s.locationEngine.AddExact(statusHandler.Path(), statusHandler.ServeHTTP, false); err != nil { if regErr := s.locationEngine.AddExact(statusHandler.Path(), statusHandler.ServeHTTP, false); regErr != nil {
if err := s.handleRegistrationError("status", statusHandler.Path(), err); err != nil { if err := s.handleRegistrationError("status", statusHandler.Path(), regErr); err != nil {
return err return err
} }
} }
@ -480,13 +482,13 @@ func (s *Server) startSingleMode() error {
if err != nil { if err != nil {
logging.Error().Msg("Failed to create pprof handler: " + err.Error()) logging.Error().Msg("Failed to create pprof handler: " + err.Error())
} else { } else {
if err := s.locationEngine.AddExact(pprofHandler.Path(), pprofHandler.ServeHTTP, false); err != nil { if regErr := s.locationEngine.AddExact(pprofHandler.Path(), pprofHandler.ServeHTTP, false); regErr != nil {
if err := s.handleRegistrationError("pprof", pprofHandler.Path(), err); err != nil { if err := s.handleRegistrationError("pprof", pprofHandler.Path(), regErr); err != nil {
return err return err
} }
} }
if err := s.locationEngine.AddPrefixPriority(pprofHandler.Path()+"/", pprofHandler.ServeHTTP, false); err != nil { if regErr := s.locationEngine.AddPrefixPriority(pprofHandler.Path()+"/", pprofHandler.ServeHTTP, false); regErr != nil {
if err := s.handleRegistrationError("pprof", pprofHandler.Path()+"/", err); err != nil { if err := s.handleRegistrationError("pprof", pprofHandler.Path()+"/", regErr); err != nil {
return err return err
} }
} }
@ -498,8 +500,8 @@ func (s *Server) startSingleMode() error {
if err != nil { if err != nil {
logging.Error().Msg("Failed to create cache purge handler: " + err.Error()) logging.Error().Msg("Failed to create cache purge handler: " + err.Error())
} else { } else {
if err := s.locationEngine.AddExact(purgeHandler.Path(), purgeHandler.ServeHTTP, false); err != nil { if regErr := s.locationEngine.AddExact(purgeHandler.Path(), purgeHandler.ServeHTTP, false); regErr != nil {
if err := s.handleRegistrationError("cache-purge", purgeHandler.Path(), err); err != nil { if err := s.handleRegistrationError("cache-purge", purgeHandler.Path(), regErr); err != nil {
return err return err
} }
} }
@ -650,7 +652,7 @@ func (s *Server) startMultiServerMode() error {
serverCfg := &s.config.Servers[i] serverCfg := &s.config.Servers[i]
ln, err := s.createListener(serverCfg) ln, err := s.createListener(serverCfg)
if err != nil { if err != nil {
for j := 0; j < i; j++ { for j := range i {
if s.listeners[j] != nil { if s.listeners[j] != nil {
_ = s.listeners[j].Close() _ = s.listeners[j].Close()
} }

View File

@ -136,7 +136,7 @@ func (l *leastConn) Select(targets []*Target) *Target {
if !t.healthy.Load() { if !t.healthy.Load() {
continue continue
} }
conns := atomic.LoadInt64(&t.conns) conns := t.conns.Load()
if selected == nil || conns < minConns { if selected == nil || conns < minConns {
selected = t selected = t
minConns = conns minConns = conns
@ -293,7 +293,7 @@ type Server struct {
// upstreams 上游配置映射 // upstreams 上游配置映射
upstreams map[string]*Upstream upstreams map[string]*Upstream
// connCount 当前连接数 // connCount 当前连接数
connCount int64 connCount atomic.Int64
// mu 读写锁,保护并发访问 // mu 读写锁,保护并发访问
mu sync.RWMutex mu sync.RWMutex
// running 运行状态标志 // running 运行状态标志
@ -329,7 +329,7 @@ type Target struct {
// healthy 健康状态 // healthy 健康状态
healthy atomic.Bool healthy atomic.Bool
// conns 当前连接数 // conns 当前连接数
conns int64 conns atomic.Int64
} }
// HealthChecker Stream 健康检查器。 // HealthChecker Stream 健康检查器。
@ -528,7 +528,7 @@ func (s *Server) acceptLoop(addr string, listener net.Listener) {
continue continue
} }
atomic.AddInt64(&s.connCount, 1) s.connCount.Add(1)
go s.handleConnection(conn, addr) go s.handleConnection(conn, addr)
} }
} }
@ -547,7 +547,7 @@ func (s *Server) acceptLoop(addr string, listener net.Listener) {
func (s *Server) handleConnection(clientConn net.Conn, _ string) { func (s *Server) handleConnection(clientConn net.Conn, _ string) {
defer func() { defer func() {
_ = clientConn.Close() _ = clientConn.Close()
atomic.AddInt64(&s.connCount, -1) s.connCount.Add(-1)
}() }()
s.mu.RLock() s.mu.RLock()
@ -569,8 +569,8 @@ func (s *Server) handleConnection(clientConn net.Conn, _ string) {
return // 无可用目标 return // 无可用目标
} }
atomic.AddInt64(&target.conns, 1) target.conns.Add(1)
defer func() { atomic.AddInt64(&target.conns, -1) }() defer func() { target.conns.Add(-1) }()
// 连接目标 // 连接目标
targetConn, err := net.DialTimeout("tcp", target.addr, 10*time.Second) targetConn, err := net.DialTimeout("tcp", target.addr, 10*time.Second)
@ -808,7 +808,7 @@ func (s *udpServer) getOrCreateSession(clientAddr *net.UDPAddr) (*udpSession, er
return nil, err return nil, err
} }
atomic.AddInt64(&target.conns, 1) target.conns.Add(1)
// 创建新会话 // 创建新会话
session = &udpSession{ session = &udpSession{
@ -855,7 +855,7 @@ func (sess *udpSession) close() {
_ = sess.targetConn.Close() _ = sess.targetConn.Close()
} }
if sess.target != nil { if sess.target != nil {
atomic.AddInt64(&sess.target.conns, -1) sess.target.conns.Add(-1)
} }
}) })
} }

View File

@ -260,8 +260,8 @@ func BenchmarkStreamBalancerSelect(b *testing.B) {
targets[i] = &Target{ targets[i] = &Target{
addr: fmt.Sprintf("backend%d:8080", i), addr: fmt.Sprintf("backend%d:8080", i),
weight: i + 1, weight: i + 1,
conns: int64(i * 10), // 模拟不同连接数
} }
targets[i].conns.Store(int64(i * 10))
targets[i].healthy.Store(true) targets[i].healthy.Store(true)
} }
@ -353,8 +353,8 @@ func BenchmarkStreamLeastConnWithVaryingConns(b *testing.B) {
targets[i] = &Target{ targets[i] = &Target{
addr: fmt.Sprintf("backend%d:8080", i), addr: fmt.Sprintf("backend%d:8080", i),
weight: 1, weight: 1,
conns: conns,
} }
targets[i].conns.Store(conns)
targets[i].healthy.Store(true) targets[i].healthy.Store(true)
} }

View File

@ -13,7 +13,6 @@ package stream
import ( import (
"net" "net"
"sync" "sync"
"sync/atomic"
"testing" "testing"
"time" "time"
) )
@ -96,10 +95,13 @@ func TestRoundRobinBalancer(t *testing.T) {
func TestLeastConnBalancer(t *testing.T) { func TestLeastConnBalancer(t *testing.T) {
targets := []*Target{ targets := []*Target{
{addr: "localhost:8001", conns: 5}, {addr: "localhost:8001"},
{addr: "localhost:8002", conns: 2}, {addr: "localhost:8002"},
{addr: "localhost:8003", conns: 8}, {addr: "localhost:8003"},
} }
targets[0].conns.Store(5)
targets[1].conns.Store(2)
targets[2].conns.Store(8)
for _, t := range targets { for _, t := range targets {
t.healthy.Store(true) t.healthy.Store(true)
} }
@ -249,12 +251,12 @@ func TestConcurrentConnections(t *testing.T) {
var wg sync.WaitGroup var wg sync.WaitGroup
for range 100 { for range 100 {
wg.Go(func() { wg.Go(func() {
atomic.AddInt64(&s.connCount, 1) s.connCount.Add(1)
}) })
} }
wg.Wait() wg.Wait()
if s.connCount != 100 { if s.connCount.Load() != 100 {
t.Errorf("Expected 100 connections, got %d", s.connCount) t.Errorf("Expected 100 connections, got %d", s.connCount)
} }
} }
@ -335,10 +337,13 @@ func TestRoundRobinBalancerWithSingleTarget(t *testing.T) {
func TestLeastConnBalancerWithTie(t *testing.T) { func TestLeastConnBalancerWithTie(t *testing.T) {
lc := newLeastConn() lc := newLeastConn()
targets := []*Target{ targets := []*Target{
{addr: "backend1:8080", conns: 5}, {addr: "backend1:8080"},
{addr: "backend2:8080", conns: 5}, {addr: "backend2:8080"},
{addr: "backend3:8080", conns: 5}, {addr: "backend3:8080"},
} }
targets[0].conns.Store(5)
targets[1].conns.Store(5)
targets[2].conns.Store(5)
for _, t := range targets { for _, t := range targets {
t.healthy.Store(true) t.healthy.Store(true)
} }