lolly/docs/plan.md
xfy 6ae7e32ef1 feat(proxy,loadbalance): 实现反向代理和负载均衡模块
实现 Phase 3 核心功能:
- loadbalance: 轮询、加权轮询、最少连接、IP哈希四种算法
- proxy: HTTP 反向代理、健康检查、故障转移
- 所有实现均为并发安全,使用 atomic 操作

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-02 17:06:29 +08:00

37 KiB
Raw Blame History

Lolly 实现计划

概述

目标:创建一个类似 nginx 的高性能 HTTP 服务器,纯 Go 实现YAML 配置,单二进制运行。

核心原则

  • 不是 1:1 复刻 nginx而是更现代、更易用
  • 充分利用 Go 特性goroutine、channel、标准库
  • 功能完整性优先于极致性能
  • 选择"更简单易用"的设计方案

第一阶段:项目骨架与配置系统

目标

搭建项目基础结构,实现配置解析和命令行工具。

任务列表

1.1 项目目录结构

lolly/
├── main.go           # 程序入口
├── internal/
│   ├── config/                 # 配置解析模块
│   ├── server/                 # HTTP 服务器核心
│   ├── handler/                # 请求处理器
│   ├── middleware/             # 中间件系统(框架)
│   │   ├── security/           # 安全中间件access、ratelimit、auth
│   │   ├── compression/        # 压缩中间件gzip、brotli
│   │   ├── logging/            # 日志中间件
│   │   └── rewrite/            # URL 重写中间件
│   ├── proxy/                  # 反向代理模块
│   ├── loadbalance/            # 负载均衡模块
│   ├── ssl/                    # SSL/TLS 模块
│   ├── cache/                  # 缓存模块
│   ├── stream/                 # TCP/UDP Stream 代理
│   └── logging/                # 日志系统核心
├── pkg/
│   └── utils/                  # 公共工具函数
├── go.mod
├── go.sum
├── Makefile                    # 构建脚本
└── README.md

关键文件

  • main.go - 入口点,初始化和启动逻辑
  • internal/config/config.go - 配置结构体定义和解析
  • internal/middleware/middleware.go - 中间件框架接口定义

1.2 YAML 配置解析

配置结构体设计

// internal/config/config.go

// Config 根配置结构
type Config struct {
    DefaultServer ServerConfig     `yaml:"server"`      // 默认服务器配置(单服务器场景)
    Servers       []ServerConfig   `yaml:"servers"`     // 多虚拟主机配置(可选)
    Logging       LoggingConfig    `yaml:"logging"`
    Performance   PerformanceConfig `yaml:"performance"`
}

// 配置字段语义说明:
// - 若只配置 `server` 字段,则作为单一服务器运行
// - 若配置 `servers` 字段,则按虚拟主机模式运行(按 Host 头匹配)
// - 若同时配置两者,`server` 作为默认 fallback 主机,`servers` 按名称匹配
// - 建议使用场景:简单部署用 `server`,多站点部署用 `servers`

// ServerConfig 服务器配置
type ServerConfig struct {
    Listen      string         `yaml:"listen"`       // 监听地址 ":8080"
    Name        string         `yaml:"name"`         // server_name
    Static      StaticConfig   `yaml:"static"`       // 静态文件
    Proxy       []ProxyConfig  `yaml:"proxy"`        // 反向代理规则
    SSL         SSLConfig      `yaml:"ssl"`          // SSL 配置
}

// StaticConfig 静态文件配置
type StaticConfig struct {
    Root   string   `yaml:"root"`    // 根目录
    Index  []string `yaml:"index"`   // 索引文件
}

// ProxyConfig 反向代理配置
type ProxyConfig struct {
    Path        string   `yaml:"path"`         // 路径匹配
    Target      string   `yaml:"target"`       // 目标地址
    LoadBalance string   `yaml:"load_balance"` // 负载均衡算法
}

实现要点

  • 使用 gopkg.in/yaml.v3 解析 YAML
  • 支持配置文件路径命令行参数 -c/--config
  • 配置验证:必填字段检查、路径有效性
  • 默认配置:最小配置即可运行

1.3 命令行工具

支持的命令

lolly                    # 启动服务器(默认配置)
lolly -c /path/to.yaml   # 指定配置文件
lolly -v                 # 显示版本

实现方式

  • 使用 flag 标准库处理参数
  • 信号处理:SIGTERMSIGINTSIGHUP

验证方法

# 构建测试
make build

# 版本显示
./lolly -v

第二阶段HTTP 核心功能

目标

实现基础 HTTP 服务器、静态文件服务、请求路由、基础日志系统。

技术选型

HTTP 库:使用 fasthttp 替代 net/http

选择理由

  • 高性能:比 net/http 快 6 倍
  • 零分配热点路径无内存分配GC 压力最小
  • 原生支持高性能场景:无需额外优化

路由库:使用 fasthttp/router

性能对比

特点 性能
fasthttp 零分配,高性能
net/http 标准库,通用

关键差异

net/http fasthttp
http.Handler 接口 RequestHandler 函数
ServeMux 内置路由 无,需 router 库
http.Request 对象 *fasthttp.RequestCtx
http.ResponseWriter ctx 同时处理读写

任务列表

2.0 中间件框架(前置依赖)

实现

// internal/middleware/middleware.go

import "github.com/valyala/fasthttp"

// Middleware 中间件接口
type Middleware interface {
    Name() string
    Process(next fasthttp.RequestHandler) fasthttp.RequestHandler
}

// Chain 中间件链
type Chain struct {
    middlewares []Middleware
}

// Apply 应用中间件链
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)
    }
    return handler
}

设计要点

  • 定义统一的中间件接口,所有中间件实现 RequestHandler 函数签名
  • 支持链式组合,按注册顺序逆序包装(从后往前)
  • Phase 2 建立框架,后续阶段填充具体中间件实现

2.1 基础 HTTP 服务器

核心实现

// internal/server/server.go

import "github.com/valyala/fasthttp"

// Server HTTP 服务器
type Server struct {
    config     *config.Config
    fastServer *fasthttp.Server
    handler    fasthttp.RequestHandler
    running    bool
}

// Start 启动服务器
func (s *Server) Start() error {
    s.fastServer = &fasthttp.Server{
        Handler:            s.handler,
        ReadTimeout:        s.config.Server.ReadTimeout,
        WriteTimeout:       s.config.Server.WriteTimeout,
        IdleTimeout:        s.config.Server.IdleTimeout,
        MaxConnsPerIP:      s.config.Server.MaxConnsPerIP,
        MaxRequestsPerConn: s.config.Server.MaxRequestsPerConn,
    }
    return s.fastServer.ListenAndServe(s.config.Server.Listen)
}

// Stop 快速停止服务器
func (s *Server) Stop() error {
    return s.fastServer.Shutdown()
}

// GracefulStop 优雅停止(等待请求完成)
func (s *Server) GracefulStop(timeout time.Duration) error {
    // fasthttp 的 Shutdown 本身就是优雅关闭
    return s.fastServer.Shutdown()
}

实现要点

  • 使用 fasthttp.Server 配置超时和连接限制
  • 优雅关闭:Shutdown() 方法自动等待请求完成
  • 配置项:ReadTimeoutWriteTimeoutIdleTimeoutMaxConnsPerIP

2.2 静态文件服务

实现

// internal/handler/static.go

import "github.com/valyala/fasthttp"

// StaticHandler 静态文件处理器
type StaticHandler struct {
    root  string
    index []string
}

// Handle 处理静态文件请求
func (h *StaticHandler) Handle(ctx *fasthttp.RequestCtx) {
    path := string(ctx.Path())

    // 安全检查:防止目录遍历
    if strings.Contains(path, "..") {
        ctx.Error("Forbidden", fasthttp.StatusForbidden)
        return
    }

    // 拼接文件路径
    filePath := filepath.Join(h.root, path)

    // 尝试索引文件
    if info, err := os.Stat(filePath); err == nil && info.IsDir() {
        for _, idx := range h.index {
            idxPath := filepath.Join(filePath, idx)
            if fasthttp.ServeFile(ctx, idxPath) == nil {
                return
            }
        }
    }

    // 直接返回文件
    fasthttp.ServeFile(ctx, filePath)
}

功能清单

  • 文件路径安全检查(防止目录遍历)
  • MIME 类型自动识别fasthttp 内置)
  • 索引文件支持index.html、index.htm
  • Range 请求支持fasthttp 内置)
  • 文件缓存优化(可选)

2.3 请求路由

路由库:使用 fasthttp/router,基于 radix tree 高效匹配。

实现

// internal/handler/router.go

import (
    "github.com/valyala/fasthttp"
    "github.com/fasthttp/router"
)

// Router 请求路由器
type Router struct {
    router *router.Router
}

// NewRouter 创建路由器
func NewRouter() *Router {
    return &Router{
        router: router.New(),
    }
}

// Register 注册路由
func (r *Router) Register(path string, handler fasthttp.RequestHandler) {
    r.router.GET(path, handler)
    r.router.POST(path, handler)
    r.router.PUT(path, handler)
    r.router.DELETE(path, handler)
}

// Handler 返回路由处理器
func (r *Router) Handler() fasthttp.RequestHandler {
    return r.router.Handler
}

fasthttp/router 匹配类型

类型 语法 示例
Named {name} /user/{id}
Optional {name?} /search/{q?}
Regex {name:regex} /user/{id:[0-9]+}
Catch-All {filepath:*} /files/{filepath:*}

参数提取

func handler(ctx *fasthttp.RequestCtx) {
    id := ctx.UserValue("id")  // 获取路由参数
}

2.4 多虚拟主机支持

实现

// internal/server/vhost.go

import "github.com/valyala/fasthttp"

// VHostManager 虚拟主机管理器
type VHostManager struct {
    hosts       map[string]*VirtualHost  // 按 server_name 索引
    defaultHost *VirtualHost             // 默认主机
}

// VirtualHost 虚拟主机
type VirtualHost struct {
    name    string
    handler fasthttp.RequestHandler
}

// Handler 返回虚拟主机选择器
func (v *VHostManager) Handler() fasthttp.RequestHandler {
    return func(ctx *fasthttp.RequestCtx) {
        host := string(ctx.Host())
        if vhost, ok := v.hosts[host]; ok {
            vhost.handler(ctx)
        } else if v.defaultHost != nil {
            v.defaultHost.handler(ctx)
        } else {
            ctx.Error("Host not found", fasthttp.StatusNotFound)
        }
    }
}

功能

  • Host 头选择虚拟主机
  • 默认主机 fallback
  • SNI 支持SSLPhase 4

2.5 基础日志系统Phase 2 必需)

原因:调试 Phase 2-4 功能需要日志支持,将日志系统基础版本提前实现。

选型:使用 zerolog 作为日志库。

选择理由

  • 零分配:高并发场景 GC 压力最小,性能最优
  • JSON 输出便于日志采集系统ELK、Loki解析
  • API 简洁:链式调用风格,开发体验好
  • 灵活输出:支持 stdout/stderr/文件,开发模式可选 ConsoleWriter 美化

性能对比10 条日志,禁用输出):

ns/op allocs/op
zerolog ~40ns 0
zap (structured) ~50ns 0
slog (Go 1.21+) ~200ns 5+
logrus ~2000ns 23

实现

// internal/logging/logging.go

import "github.com/rs/zerolog"

// 全局日志实例
var log zerolog.Logger

// Init 初始化日志系统
func Init(level string, pretty bool) {
    l := parseLevel(level)
    if pretty {
        // 开发模式:带颜色和格式化(性能较差,仅开发用)
        log = zerolog.New(zerolog.ConsoleWriter{Out: os.Stdout})
    } else {
        // 生产模式JSON 输出
        log = zerolog.New(os.Stdout).Level(l).With().Timestamp().Logger()
    }
}

// AccessLogger 访问日志(基础版)
func LogAccess(r *http.Request, status int, size int64, duration time.Duration) {
    log.Info().
        Str("method", r.Method).
        Str("path", r.URL.Path).
        Int("status", status).
        Int64("size", size).
        Dur("duration", duration).
        Msg("request")
}

Phase 2 实现范围

  • 基础请求日志:记录请求方法、路径、状态码
  • 控制台输出开发阶段便于调试ConsoleWriter 美化)
  • Phase 5 将扩展为完整日志系统(文件输出、自定义格式、访问/错误日志分离)

验证方法

# 启动服务器
./lolly -c lolly.yaml

# 静态文件测试
curl http://localhost:8080/index.html

# 路由测试
curl http://localhost:8080/static/test.txt
curl http://localhost:8080/api/health  # 应返回 404代理未实现

第三阶段:反向代理与负载均衡

目标

实现反向代理功能、多种负载均衡算法、健康检查。

任务列表

3.1 反向代理核心

实现(基于 fasthttp

// internal/proxy/proxy.go

import "github.com/valyala/fasthttp"

// Proxy 反向代理
type Proxy struct {
    targets    []*Target
    clients    map[string]*fasthttp.HostClient  // 每个目标一个 HostClient
    balancer   Balancer
}

// Target 后端目标
type Target struct {
    URL         string   // 目标地址,如 "http://backend1:8080"
    Weight      int
    Healthy     bool
    Connections int64    // 当前连接数(原子操作)
}

// HostClient fasthttp 客户端(连接池)
// 每个 Target 对应一个 HostClient自动管理连接池

功能清单

  • 请求转发:修改请求头、请求体
  • 响应处理:修改响应头
  • 超时配置连接超时、响应超时fasthttp.HostClient 配置)
  • WebSocket 支持Upgrade 协议检测和转发
  • 错误处理:后端不可用时的响应

3.2 负载均衡算法

实现

// internal/loadbalance/balancer.go

// Balancer 负载均衡器接口
type Balancer interface {
    Select(targets []*Target) *Target
}

// RoundRobin 轮询算法
type RoundRobin struct {
    current uint64
}

// WeightedRoundRobin 权重轮询
type WeightedRoundRobin struct {
    weights []int
    current int
}

// LeastConnections 最少连接
type LeastConnections struct{}

// IPHash IP 哈希
type IPHash struct{}

算法实现

算法 说明
round_robin 简单轮询
weighted_round_robin 按权重轮询
least_conn 选择连接数最少的目标
ip_hash 按客户端 IP 哈希固定目标

3.3 健康检查

实现

// internal/proxy/health.go

// HealthChecker 健康检查器
type HealthChecker struct {
    interval   time.Duration
    timeout    time.Duration
    path       string  // 健康检查路径
    targets    []*Target
}

// Check 执行健康检查
func (h *HealthChecker) Check()

// Start 后台定期检查
func (h *HealthChecker) Start()

类型

  • 被动检查:请求失败时标记不健康
  • 主动检查:定期发送探测请求

配置示例

proxy:
  - path: /api
    targets:
      - url: http://backend1:8080
        weight: 3
      - url: http://backend2:8080
        weight: 1
    load_balance: weighted_round_robin
    health_check:
      interval: 10s
      path: /health
      timeout: 5s

3.4 代理缓存(可选)

实现

// internal/cache/proxy_cache.go

// ProxyCache 代理响应缓存
type ProxyCache struct {
    storage CacheStorage
    rules   []CacheRule
}

验证方法

# 启动后端服务(用于测试)
# backend1: python3 -m http.server 8001
# backend2: python3 -m http.server 8002

# 配置代理
# lolly.yaml:
#   proxy:
#     - path: /api
#       targets: [http://localhost:8001, http://localhost:8002]

# 测试代理
curl http://localhost:8080/api/test

# 测试负载均衡(多次请求)
for i in {1..10}; do curl http://localhost:8080/api/test; done

第四阶段:安全与 SSL/TLS

目标

实现 HTTPS 支持、访问控制、请求限制。

任务列表

4.1 SSL/TLS 支持

实现

// internal/ssl/ssl.go

// SSLConfig SSL 配置
type SSLConfig struct {
    Cert         string   `yaml:"cert"`          // 证书路径
    Key          string   `yaml:"key"`           // 私钥路径
    CertChain    string   `yaml:"cert_chain"`    // 证书链路径(可选,用于中间证书)
    Protocols    []string `yaml:"protocols"`     // TLS 版本,默认 ["TLSv1.2", "TLSv1.3"]
    Ciphers      []string `yaml:"ciphers"`       // 加密套件(仅 TLS 1.2 有效)
    OCSPStapling bool     `yaml:"ocsp_stapling"` // OCSP Stapling 支持(默认 false
}

// TLSManager TLS 管理器
type TLSManager struct {
    configs map[string]*tls.Config  // 按 server_name
}

安全默认配置

  • TLS 版本:默认仅允许 TLSv1.2 和 TLSv1.3强制禁用 TLSv1.0/TLSv1.1
  • 加密套件默认值TLS 1.2,按优先级排序):
    # 默认安全加密套件,无需手动配置
    ciphers:
      - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 # 推荐,性能好
      - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 # 推荐,更安全
      - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 # 推荐,移动端友好
      - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 # ECDSA 证书专用
      - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 # ECDSA 证书专用
      - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 # ECDSA 证书专用
    
  • TLS 1.3:自动使用 Go 标准库的安全套件,配置无效

安全校验

  • 启用 Basic Auth 时,强制要求 SSL 配置,否则拒绝启动
  • 拒绝配置不安全的加密套件(如 RC4、DES、3DES

功能清单

  • 证书加载PEM 格式,支持证书链合并
  • TLS 版本控制TLSv1.2、TLSv1.3默认禁用不安全版本
  • 加密套件配置:提供安全默认值,拒绝不安全套件
  • SSL 会话缓存减少握手开销LRU 缓存,默认 128 条)
  • SNI 支持:多证书,通过 GetCertificate 回调实现
  • HTTP/2 自动启用TLS 时自动启用
  • OCSP Stapling减少客户端 CA 查询延迟和隐私风险

4.2 IP 访问控制

实现

// internal/middleware/security/access.go

// AccessControl IP 访问控制
type AccessControl struct {
    allowList []net.IPNet
    denyList  []net.IPNet
    default   Action  // 默认动作
}

// Check 检查 IP 是否允许
func (a *AccessControl) Check(ip net.IP) bool

性能优化建议

  • 使用 CIDR 树结构radix tree优化大规模 ACL 匹配
  • 预编译匹配规则,减少运行时开销
  • 支持 IPv4 和 IPv6 双栈匹配

配置示例

security:
  access:
    allow: [192.168.1.0/24, 10.0.0.0/8, "2001:db8::/32"] # 支持 IPv6
    deny: [192.168.2.100/32]
    default: deny
    # 可选:使用高性能匹配模式
    optimize: true # 启用 CIDR 树优化(适用于 >100 条规则)

4.3 请求限制

实现

// internal/middleware/security/ratelimit.go

// RateLimiter 速率限制器(令牌桶算法)
type RateLimiter struct {
    rate     int           // 令牌生成速率(请求/秒)
    burst    int           // 桶容量(突发流量上限)
    buckets  map[string]*TokenBucket
    mu       sync.RWMutex
}

// TokenBucket 令牌桶
type TokenBucket struct {
    tokens     float64
    lastUpdate time.Time
}

// SlidingWindowLimiter 滑动窗口限流器(可选,解决边界突发问题)
type SlidingWindowLimiter struct {
    window    time.Duration
    limit     int
    requests  map[string][]time.Time
}

// ConnLimiter 连接数限制器
type ConnLimiter struct {
    max      int
    current  int
    mu       sync.Mutex
}

算法选择

算法 适用场景 特点
令牌桶 (Token Bucket) API 请求限流 允许突发流量,推荐默认使用
滑动窗口 (Sliding Window) 精确限流 解决固定窗口边界问题,无突发

功能

  • 请求速率限制(limit_req
  • 连接数限制(limit_conn
  • 按 IP 或按 key 限制
  • 超限响应429 Too Many Requests
  • 支持 Retry-After 响应头告知等待时间

4.4 基础认证

实现

// internal/middleware/security/auth.go

// BasicAuth 基础认证
type BasicAuth struct {
    users     map[string]string  // username -> hashed_password
    algorithm HashAlgorithm      // 哈希算法bcrypt默认或 argon2id
    realm     string
    requireTLS bool              // 强制 HTTPS默认 true
}

// HashAlgorithm 哈希算法类型
type HashAlgorithm int
const (
    HashBcrypt HashAlgorithm = iota  // bcrypt默认推荐
    HashArgon2id                       // Argon2id更安全计算密集
)

// Authenticate 验证认证信息
func (b *BasicAuth) Authenticate(r *http.Request) bool

安全要求

  • 强制 HTTPS:启用 Basic Auth 时必须配置 SSL否则拒绝启动
  • 安全哈希:默认使用 bcrypt成本因子 12可选 Argon2id
  • 弃用 apr1:不再支持不安全的 MD5-based apr1 哈希
  • 密码强度:配置验证,拒绝弱密码

配置示例

security:
  auth:
    type: basic
    require_tls: true # 强制 HTTPS默认 true
    algorithm: bcrypt # bcrypt默认或 argon2id
    users:
      - name: admin
        password: $2b$12$... # bcrypt 哈希(推荐)
      - name: api_user
        password: $argon2id$... # Argon2id 哈希(可选)
    realm: "Restricted Area"
    min_password_length: 12 # 密码最小长度

4.5 安全头部

实现

// internal/middleware/security/headers.go

// SecurityHeaders 安全头部配置
type SecurityHeaders struct {
    XFrameOptions        string `yaml:"x_frame_options"`         // DENY/SAMEORIGIN/ALLOW-FROM
    XContentTypeOptions  string `yaml:"x_content_type_options"`  // nosniff默认
    ContentSecurityPolicy string `yaml:"content_security_policy"` // CSP 策略
    HSTS                 HSTSConfig `yaml:"hsts"`               // HSTS 配置
    ReferrerPolicy       string `yaml:"referrer_policy"`        // 推荐值
    PermissionsPolicy    string `yaml:"permissions_policy"`     // 权限策略
}

// HSTSConfig HSTS 配置
type HSTSConfig struct {
    MaxAge            int  `yaml:"max_age"`             // 过期时间(秒),默认 315360001年
    IncludeSubDomains bool `yaml:"include_sub_domains"` // 包含子域名,默认 true
    Preload           bool `yaml:"preload"`             // HSTS 预加载列表,默认 false
}

默认安全头部

头部 默认值 说明
X-Frame-Options DENY 防止点击劫持,可配置为 SAMEORIGIN
X-Content-Type-Options nosniff 防止 MIME 类型嗅探
Content-Security-Policy 可配置 关键:防止 XSS 攻击
Strict-Transport-Security max-age=31536000; includeSubDomains HSTS强制 HTTPS
Referrer-Policy strict-origin-when-cross-origin 控制引用信息泄露
Permissions-Policy 可配置 控制浏览器功能权限

注意X-XSS-Protection 已被现代浏览器弃用,不再默认添加,重点依赖 CSP 防护。

配置示例

security:
  headers:
    x_frame_options: SAMEORIGIN # 或 DENY默认
    content_security_policy: "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'"
    hsts:
      max_age: 31536000 # 1年
      include_sub_domains: true # 包含子域名
      preload: false # 不加入预加载列表(需用户显式启用)
    referrer_policy: strict-origin-when-cross-origin
    permissions_policy: "geolocation=(), microphone=(), camera=()"

验证方法

# HTTPS 测试
curl -k https://localhost:8443/

# IP 访问控制测试
curl --interface 192.168.1.100 http://localhost:8080/  # 应允许
curl --interface 192.168.2.100 http://localhost:8080/  # 应拒绝

# 速率限制测试
for i in {1..20}; do curl http://localhost:8080/; done  # 部分应返回 429

# 基础认证测试
curl -u admin:password http://localhost:8080/protected/

第五阶段:增强功能

目标

实现 URL 重写、压缩、缓存、日志系统。

任务列表

5.1 URL 重写

实现

// internal/rewrite/rewrite.go

// RewriteRule 重写规则
type RewriteRule struct {
    Pattern     string  // 匹配模式
    Replacement string  // 替换目标
    Flag        RewriteFlag  // last/redirect/break
}

type RewriteFlag int
const (
    FlagLast     RewriteFlag = iota  // 继续匹配其他规则
    FlagRedirect                      // 302 重定向
    FlagPermanent                     // 301 重定向
    FlagBreak                         // 停止匹配
)

配置示例

rewrite:
  - pattern: "^/old/(.*)$"
    replacement: "/new/$1"
    flag: permanent # 301
  - pattern: "^/api/v1/(.*)$"
    replacement: "/api/v2/$1"
    flag: last

5.2 Gzip/Brotli 压缩

实现

// internal/compression/compression.go

// CompressionHandler 压缩中间件
type CompressionHandler struct {
    types    []string  // 压缩的 MIME 类型
    level    int       // 压缩级别
    minSize  int       // 最小压缩大小
}

配置示例

compression:
  type: gzip # gzip/brotli/both
  level: 6 # 1-9
  min_size: 1024 # 最小 1KB 才压缩
  types: [text/html, text/css, application/json]

5.3 缓存系统

静态文件缓存

// internal/cache/file_cache.go

// FileCache 文件描述符缓存
type FileCache struct {
    maxEntries int
    maxSize    int64         // 内存上限(新增)
    inactive   time.Duration
    entries    map[string]*FileEntry
    lruList    *list.List    // LRU 淘汰链表(新增)
}

// FileEntry 缓存条目
type FileEntry struct {
    fd         *os.File
    size       int64
    modTime    time.Time
    lastAccess time.Time
}

代理响应缓存

// internal/cache/proxy_cache.go

// ProxyCache 代理缓存
type ProxyCache struct {
    storage    Storage         // 内存/磁盘存储
    rules      []CacheRule
    maxAge     time.Duration
    cacheLock  bool            // 缓存锁开关(默认 true
    lock       *sync.RWMutex   // 缓存锁,防止击穿
    pending    map[string]*chan struct{} // 正在生成的缓存项
}

// CacheRule 缓存规则
type CacheRule struct {
    Path      string
    Methods   []string
    Statuses  []int           // 可缓存的响应状态码
    MaxAge    time.Duration
}

缓存锁机制(防击穿)

  • 当多个请求同时请求同一个未缓存的资源时,只让一个请求去后端获取
  • 其他请求等待第一个请求完成后从缓存读取
  • 防止缓存击穿导致后端压力骤增

配置示例

cache:
  file:
    max_entries: 10000
    max_size: 256MB # 内存上限
    inactive: 20s
    lru_eviction: true # 启用 LRU 淘汰

  proxy:
    enabled: true
    storage: memory # memory/disk
    max_size: 1GB
    cache_lock: true # 防止缓存击穿
    stale_while_revalidate: 60s # 过期缓存复用
    rules:
      - path: /api/cacheable
        methods: [GET]
        statuses: [200, 301, 302]
        max_age: 10m

5.4 日志系统

扩展 Phase 2 的 zerolog 实现,增加文件输出和访问/错误日志分离。

实现

// internal/logging/logging.go

import (
    "io"
    "github.com/rs/zerolog"
)

// Logger 日志管理器
type Logger struct {
    accessLog zerolog.Logger  // 访问日志
    errorLog  zerolog.Logger  // 错误日志
}

// New 创建日志管理器
func New(cfg *LoggingConfig) *Logger {
    // 访问日志stdout 或文件
    accessOut := getOutput(cfg.Access.Path)
    accessLog := zerolog.New(accessOut).With().Timestamp().Logger()

    // 错误日志stderr 或文件
    errorOut := getOutput(cfg.Error.Path)
    errorLevel := parseLevel(cfg.Error.Level)
    errorLog := zerolog.New(errorOut).Level(errorLevel).With().Timestamp().Logger()

    return &Logger{accessLog: accessLog, errorLog: errorLog}
}

// LogAccess 记录访问日志nginx 格式变量)
func (l *Logger) LogAccess(r *http.Request, status int, size int64, duration time.Duration) {
    l.accessLog.Info().
        Str("remote_addr", r.RemoteAddr).
        Str("request", fmt.Sprintf("%s %s", r.Method, r.URL.Path)).
        Int("status", status).
        Int64("body_bytes_sent", size).
        Dur("request_time", duration).
        Msg("")
}

// getOutput 获取输出目标stdout/stderr/文件)
func getOutput(path string) io.Writer {
    if path == "" {
        return os.Stdout
    }
    f, _ := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
    return f
}

日志格式变量(支持 nginx 风格配置):

  • $remote_addr - 客户端 IP
  • $request - 请求行(方法 + 路径)
  • $status - 响应状态码
  • $body_bytes_sent - 响应体大小
  • $request_time - 请求耗时

配置示例

logging:
  access:
    path: /var/log/lolly/access.log  # 留空则输出到 stdout
    format: json                     # json 或 textPhase 2 ConsoleWriter
  error:
    path: /var/log/lolly/error.log   # 留空则输出到 stderr
    level: info                      # debug/info/warn/error

5.5 状态监控端点

实现

// internal/server/status.go

// StatusHandler 状态监控处理器
type StatusHandler struct {
    server *Server
}

// 返回数据
type Status struct {
    Connections   int
    Requests      int64
    BytesSent     int64
    BytesReceived int64
    Uptime        time.Duration
}

配置示例

monitoring:
  status:
    path: /_status # 状态端点路径
    allow: [127.0.0.1] # 仅允许本地访问

验证方法

# 重写测试
curl http://localhost:8080/old/page  # 应重定向到 /new/page

# 压缩测试
curl -H "Accept-Encoding: gzip" -I http://localhost:8080/index.html
# 应返回 Content-Encoding: gzip

# 缓存测试
curl -I http://localhost:8080/static/test.txt
# 应返回缓存相关头部

# 日志测试
cat /var/log/lolly/access.log

# 状态监控测试
curl http://localhost:8080/_status

第六阶段:高级功能

目标

实现 TCP/UDP Stream 代理、性能优化、优雅升级。

任务列表

6.1 TCP/UDP Stream 代理

实现

// internal/stream/stream.go

// StreamServer TCP/UDP 代理服务器
type StreamServer struct {
    listeners map[string]*net.Listener
    upstreams map[string]*StreamUpstream
}

// StreamUpstream Stream 上游
type StreamUpstream struct {
    targets  []*StreamTarget
    balancer Balancer
}

// StreamTarget Stream 目标
type StreamTarget struct {
    addr      string
    healthy   bool
}

配置示例

stream:
  - listen: 3306
    protocol: tcp
    upstream:
      targets: [mysql1:3306, mysql2:3306]
      load_balance: round_robin
  - listen: 53
    protocol: udp
    upstream:
      targets: [dns1:53, dns2:53]

6.2 优雅升级(热升级)

实现

// internal/server/upgrade.go

// GracefulUpgrade 优雅升级
func GracefulUpgrade(newBinary string) error

// 逻辑:
// 1. 启动新进程,继承监听 socket
// 2. 新进程开始接受新连接
// 3. 旧进程停止接受新连接,完成现有请求后退出

信号处理

  • SIGUSR2:触发升级
  • SIGWINCH:优雅关闭 worker

6.3 性能优化

优化点

6.3.1 连接复用
// http.Transport 连接池配置
transport := &http.Transport{
    MaxIdleConns:        100,              // 最大空闲连接数
    MaxIdleConnsPerHost: 32,               // 每主机最大空闲连接
    IdleConnTimeout:     90 * time.Second, // 空闲连接超时
    MaxConnsPerHost:     0,                // 每主机最大连接数0=无限制)
}
6.3.2 缓冲池
// 使用 sync.Pool 实现分级缓冲池
var bufferPool = sync.Pool{
    New: func() interface{} {
        return make([]byte, 32*1024) // 32KB 缓冲区
    },
}
6.3.3 零拷贝sendfile
// internal/handler/sendfile.go

// SendFile 零拷贝文件传输(仅 Linux
// 大文件(>= 8KB使用 sendfile 系统调用
func SendFile(w http.ResponseWriter, f *os.File, offset, length int64) error {
    // Linux: syscall.Sendfile
    // macOS: syscall.Sendfile不同签名
    // Windows: syscall.TransmitFile
    // 其他平台: 降级为 io.Copy
}

// 跨平台兼容方案
// - Linux: sendfile(out_fd, in_fd, offset, count)
// - macOS: sendfile(in_fd, out_fd, offset, &len, sf_hdtr, flags)
// - Windows: TransmitFile(socket, handle, bytes_to_write, ...
// - Fallback: io.CopyBuffer
6.3.4 Goroutine 池(可选)
// internal/server/pool.go

// GoroutinePool Goroutine 池配置
type GoroutinePool struct {
    maxWorkers   int           // 最大 worker 数
    minWorkers   int           // 最小 worker 数(预热)
    idleTimeout  time.Duration // 空闲超时
    taskQueue    chan Task     // 任务队列
}

// 配置示例
performance:
  goroutine_pool:
    enabled: true          // 启用池化(高 QPS 场景推荐)
    max_workers: 10000     // 最大并发数
    min_workers: 100       // 预热 worker 数
    idle_timeout: 60s      // 空闲超时
6.3.5 对象池
// 使用 sync.Pool 复用对象
var requestPool = sync.Pool{
    New: func() interface{} {
        return new(Request)
    },
}
6.3.6 代理缓存锁(防击穿)
// internal/cache/proxy_cache.go

// ProxyCache 代理缓存(增加缓存锁)
type ProxyCache struct {
    storage   Storage
    rules     []CacheRule
    maxAge    time.Duration
    lock      *sync.RWMutex     // 缓存锁,防止缓存击穿
    pending   map[string]*chan struct{} // 正在生成的缓存项
}

// 缓存锁机制:
// 1. 请求到达时检查是否有 pending 请求
// 2. 有则等待 pending 完成
// 3. 无则创建 pending生成缓存后广播

6.4 信号处理完善

完整信号支持

信号 行为
SIGTERM/SIGINT 快速停止
SIGQUIT 优雅停止
SIGHUP 重载配置
SIGUSR1 重新打开日志
SIGUSR2 热升级

验证方法

# TCP Stream 测试
# 启动 MySQL 后端
mysql -h localhost -P 3306  # 应通过 lolly 代理连接

# 热升级测试
kill -USR2 <pid>  # 触发升级
ps aux | grep lolly  # 应有两个进程

# 性能测试
# 使用 wrk 或 ab 进行压力测试
wrk -t4 -c1000 -d30s http://localhost:8080/

文件依赖关系图

Phase 1:
  cmd/lolly/main.go → internal/config/config.go
  cmd/lolly/main.go → internal/middleware/middleware.go

Phase 2:
  internal/server/server.go → internal/config/config.go
  internal/server/server.go → internal/handler/router.go
  internal/server/server.go → internal/logging/logging.go
  internal/handler/router.go → internal/handler/static.go
  internal/handler/router.go → internal/middleware/middleware.go

Phase 3:
  internal/handler/router.go → internal/proxy/proxy.go
  internal/proxy/proxy.go → internal/loadbalance/balancer.go
  internal/proxy/proxy.go → internal/proxy/health.go

Phase 4:
  internal/server/server.go → internal/ssl/ssl.go
  internal/middleware/middleware.go → internal/middleware/security/access.go
  internal/middleware/middleware.go → internal/middleware/security/ratelimit.go
  internal/middleware/middleware.go → internal/middleware/security/auth.go
  internal/middleware/middleware.go → internal/middleware/security/headers.go

Phase 5:
  internal/middleware/middleware.go → internal/middleware/rewrite/rewrite.go
  internal/middleware/middleware.go → internal/middleware/compression/compression.go
  internal/proxy/proxy.go → internal/cache/proxy_cache.go
  internal/server/server.go → internal/logging/logging.go扩展

Phase 6:
  cmd/lolly/main.go → internal/stream/stream.go
  internal/server/server.go → internal/server/upgrade.go
  internal/server/server.go → internal/server/pool.go可选

总体进度追踪

阶段 状态 主要功能
Phase 1 完成 项目骨架、配置系统
Phase 2 完成 HTTP 核心、静态文件、路由
Phase 3 完成 反向代理、负载均衡
Phase 4 待开始 SSL/TLS、安全控制
Phase 5 待开始 重写、压缩、缓存、日志
Phase 6 待开始 Stream、性能优化

Phase 2 技术选型变更

  • HTTP 库:使用 fasthttp 替代 net/http(性能提升 6 倍)
  • 日志库:使用 zerolog(零分配,~40ns/op

参考文档

详细功能参考 docs/ 目录:

  • HTTP 核心:docs/03-nginx-http-core.md
  • 代理负载均衡:docs/04-nginx-proxy-loadbalancing.md
  • SSL/HTTPSdocs/05-nginx-ssl-https.md
  • URL 重写:docs/06-nginx-rewrite.md
  • 压缩缓存:docs/07-nginx-compression-caching.md
  • 日志监控:docs/08-nginx-logging-monitoring.md
  • 安全控制:docs/09-nginx-security.md
  • Stream 代理:docs/10-nginx-stream-tcp-udp.md
  • 性能优化:docs/12-nginx-performance-tuning.md

代码注释规范docs/comments.md(必须遵循)