lolly/docs/superpowers/specs/2026-06-03-eliminate-code-redundancy-design.md
xfy ebeb258c58 docs(benchmark): add v0.4.0 baseline summary and update gitignore
- Collect baseline benchmark summary across all core modules
- Save key results to benchmarks/v0.4.0/summary.txt
- Update .gitignore to track benchmark summaries/reports
- Include performance optimization design docs and plan
2026-06-11 13:43:28 +08:00

6.7 KiB
Raw Blame History

消除代码冗余设计文档

日期: 2026-06-03
目标: 消除 lolly 项目中的代码冗余,提升可维护性和代码质量
范围: 死代码删除、重复模式重构、测试辅助函数提取


1. 问题分析

通过对代码库的静态分析(golangci-lint + dupl + unused),发现以下冗余代码:

1.1 死代码Dead Code

文件 函数/方法 行号 说明
internal/config/validate.go validateStatic() 475 validateStatics() 已内联相同逻辑,仅被测试调用
internal/http2/server.go connectionPool.get() 576 无任何引用
internal/http2/server.go connectionPool.count() 583 无任何引用
internal/middleware/bodylimit/bodylimit.go formatSize() 288 业务代码未使用,仅被测试调用;autoindex.go 有同名函数
internal/middleware/security/headers.go defaultSecurityHeaders() 295 仅被测试调用,业务代码未使用
internal/middleware/security/headers.go strictSecurityHeaders() 309 仅被测试调用,业务代码未使用
internal/middleware/security/headers.go developmentSecurityHeaders() 325 仅被测试调用,业务代码未使用
internal/ssl/ocsp.go extractCertificates() 490 仅被测试调用,业务代码未使用

排除项(经确认实际被使用):

  • setupTestLogger() - 在 app_test.go 中被调用 47 次
  • canonicalHeaderKey() - 在 server_test.go 中被调用

1.2 源文件重复模式

路由注册错误处理(internal/server/router.go

19 次重复模式proxy、static、lua 三种 handler

if err := s.locationEngine.AddXXX(path, handler, internal); err != nil {
    if err := s.handleRegistrationError("type", path, err); err != nil {
        return err
    }
}

DEBUG 日志条件检查(internal/proxy/proxy.go

5 次重复模式:

if logging.Debug().Enabled() {
    logging.Debug().Str("key", value).Msg("[PROXY] message")
}

1.3 测试文件重复代码

模式 出现次数 位置
config.ProxyConfig{...} 184 各测试文件
config.ProxyTimeout{Connect: 5 * time.Second} 85 各测试文件
targets := []*loadbalance.Target{{URL: "http://..."}} 123 各测试文件
targets[0].Healthy.Store(true) 41 各测试文件

2. 设计方案

2.1 阶段 1死代码删除

策略:直接删除未使用的函数,同时清理仅被测试调用的函数的测试代码。

处理清单

  1. validateStatic() - 删除函数,将测试迁移到测试 validateStatics()
  2. connectionPool.get() / connectionPool.count() - 直接删除
  3. formatSize() (bodylimit) - 删除函数,删除测试;autoindex.go 的同名函数保留
  4. defaultSecurityHeaders() / strictSecurityHeaders() / developmentSecurityHeaders() - 删除函数,删除测试
  5. extractCertificates() - 删除函数,删除测试

2.2 阶段 2重复模式重构

2.2.1 路由注册辅助函数

internal/server/router.go 中提取辅助函数:

// registerRoute 注册路由并处理错误
func (s *Server) registerRoute(
    locType string,
    path string,
    handler fasthttp.RequestHandler,
    internal bool,
    source string,
) error {
    var err error
    switch locType {
    case matcher.LocationTypeExact:
        err = s.locationEngine.AddExact(path, handler, internal)
    case matcher.LocationTypePrefixPriority:
        err = s.locationEngine.AddPrefixPriority(path, handler, internal)
    case matcher.LocationTypeRegex:
        err = s.locationEngine.AddRegex(path, handler, false, internal)
    case matcher.LocationTypeRegexCaseless:
        err = s.locationEngine.AddRegex(path, handler, true, internal)
    case matcher.LocationTypeNamed:
        err = s.locationEngine.AddNamed(path, handler)
    default:
        err = s.locationEngine.AddPrefix(path, handler, internal)
    }
    if err != nil {
        return s.handleRegistrationError(source, path, err)
    }
    return nil
}

2.2.2 DEBUG 日志辅助函数

internal/proxy/proxy.go 中提取辅助函数:

// proxyDebugLog 在 DEBUG 级别记录代理日志
func proxyDebugLog(msg string, kv ...interface{}) {
    if !logging.Debug().Enabled() {
        return
    }
    event := logging.Debug()
    for i := 0; i < len(kv)-1; i += 2 {
        key, ok := kv[i].(string)
        if !ok {
            continue
        }
        switch v := kv[i+1].(type) {
        case string:
            event = event.Str(key, v)
        case int:
            event = event.Int(key, v)
        case bool:
            event = event.Bool(key, v)
        }
    }
    event.Msg(msg)
}

2.3 阶段 3测试辅助函数

internal/testutil/ 包中创建辅助函数:

package testutil

import (
    "rua.plus/lolly/internal/config"
    "rua.plus/lolly/internal/loadbalance"
)

// NewTestProxyConfig 创建测试用的代理配置
func NewTestProxyConfig(path string, targets []string) *config.ProxyConfig {
    cfg := &config.ProxyConfig{
        Path:        path,
        LoadBalance: "round_robin",
        Timeout: config.ProxyTimeout{
            Connect: 5 * time.Second,
            Read:    30 * time.Second,
            Write:   30 * time.Second,
        },
    }
    // ...
    return cfg
}

// NewTestTarget 创建测试用的代理目标
func NewTestTarget(url string) *loadbalance.Target {
    return &loadbalance.Target{URL: url}
}

// NewTestHealthyTarget 创建已标记为健康的测试目标
func NewTestHealthyTarget(url string) *loadbalance.Target {
    t := NewTestTarget(url)
    t.Healthy.Store(true)
    return t
}

迁移策略

  1. 先创建辅助函数
  2. 逐步替换测试文件中的重复代码
  3. 每次替换后运行测试确保通过

3. 风险评估

风险 可能性 影响 缓解措施
删除的函数实际上被间接使用 通过 grep 确认无引用后再删除
重构引入新 bug 每次变更后运行完整测试套件
测试辅助函数改变测试语义 保持默认配置与原始代码一致

4. 验收标准

  • golangci-lint run --enable=unused ./... 无 unused 错误
  • golangci-lint run --enable=dupl ./... 源文件无 dupl 错误
  • go test ./... 全部通过
  • 代码总行数减少 >200 行
  • 测试文件中的 ProxyConfig{ 字面量减少 >50%

5. 实施顺序

  1. 阶段 1死代码 - 低风险,快速见效
  2. 阶段 2源文件重构 - 中等风险,改善可维护性
  3. 阶段 3测试辅助函数 - 低风险,最大减负