# 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 配置解析 **配置结构体设计**: ```go // 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 命令行工具 **支持的命令**: ```bash lolly # 启动服务器(默认配置) lolly -c /path/to.yaml # 指定配置文件 lolly -v # 显示版本 ``` **实现方式**: - 使用 `flag` 标准库处理参数 - 信号处理:`SIGTERM`、`SIGINT`、`SIGHUP` ### 验证方法 ```bash # 构建测试 make build # 版本显示 ./lolly -v ``` --- ## 第二阶段:HTTP 核心功能 ### 目标 实现基础 HTTP 服务器、静态文件服务、请求路由、基础日志系统。 ### 技术选型 **HTTP 库**:使用 [fasthttp](https://github.com/valyala/fasthttp) 替代 `net/http`。 **选择理由**: - **高性能**:比 net/http 快 6 倍 - **零分配**:热点路径无内存分配,GC 压力最小 - **原生支持高性能场景**:无需额外优化 **路由库**:使用 [fasthttp/router](https://github.com/fasthttp/router)。 **性能对比**: | 库 | 特点 | 性能 | |----|------|------| | fasthttp | 零分配,高性能 | ⭐⭐⭐⭐⭐ | | net/http | 标准库,通用 | ⭐⭐⭐ | **关键差异**: | net/http | fasthttp | |----------|----------| | `http.Handler` 接口 | `RequestHandler` 函数 | | `ServeMux` 内置路由 | 无,需 router 库 | | `http.Request` 对象 | `*fasthttp.RequestCtx` | | `http.ResponseWriter` | `ctx` 同时处理读写 | ### 任务列表 #### 2.0 中间件框架(前置依赖) **实现**: ```go // 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 服务器 **核心实现**: ```go // 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()` 方法自动等待请求完成 - 配置项:`ReadTimeout`、`WriteTimeout`、`IdleTimeout`、`MaxConnsPerIP` #### 2.2 静态文件服务 **实现**: ```go // 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](https://github.com/fasthttp/router),基于 radix tree 高效匹配。 **实现**: ```go // 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:*}` | **参数提取**: ```go func handler(ctx *fasthttp.RequestCtx) { id := ctx.UserValue("id") // 获取路由参数 } ``` #### 2.4 多虚拟主机支持 **实现**: ```go // 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 支持(SSL,Phase 4) #### 2.5 基础日志系统(Phase 2 必需) **原因**:调试 Phase 2-4 功能需要日志支持,将日志系统基础版本提前实现。 **选型**:使用 [zerolog](https://github.com/rs/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 | **实现**: ```go // 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 将扩展为完整日志系统(文件输出、自定义格式、访问/错误日志分离) ### 验证方法 ```bash # 启动服务器 ./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): ```go // 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 负载均衡算法 **实现**: ```go // 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 健康检查 **实现**: ```go // 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() ``` **类型**: - **被动检查**:请求失败时标记不健康 - **主动检查**:定期发送探测请求 **配置示例**: ```yaml 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 代理缓存(可选) **实现**: ```go // internal/cache/proxy_cache.go // ProxyCache 代理响应缓存 type ProxyCache struct { storage CacheStorage rules []CacheRule } ``` ### 验证方法 ```bash # 启动后端服务(用于测试) # 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 支持 **实现**: ```go // 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,按优先级排序): ```yaml # 默认安全加密套件,无需手动配置 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 访问控制 **实现**: ```go // 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 双栈匹配 **配置示例**: ```yaml 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 请求限制 **实现**: ```go // 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 基础认证 **实现**: ```go // 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 哈希 - **密码强度**:配置验证,拒绝弱密码 **配置示例**: ```yaml 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 安全头部 **实现**: ```go // 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"` // 过期时间(秒),默认 31536000(1年) 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 防护。 **配置示例**: ```yaml 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=()" ``` ### 验证方法 ```bash # 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 重写 **实现**: ```go // 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 // 停止匹配 ) ``` **配置示例**: ```yaml rewrite: - pattern: "^/old/(.*)$" replacement: "/new/$1" flag: permanent # 301 - pattern: "^/api/v1/(.*)$" replacement: "/api/v2/$1" flag: last ``` #### 5.2 Gzip/Brotli 压缩 **实现**: ```go // internal/compression/compression.go // CompressionHandler 压缩中间件 type CompressionHandler struct { types []string // 压缩的 MIME 类型 level int // 压缩级别 minSize int // 最小压缩大小 } ``` **配置示例**: ```yaml compression: type: gzip # gzip/brotli/both level: 6 # 1-9 min_size: 1024 # 最小 1KB 才压缩 types: [text/html, text/css, application/json] ``` #### 5.3 缓存系统 **静态文件缓存**: ```go // 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 } ``` **代理响应缓存**: ```go // 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 } ``` **缓存锁机制(防击穿)**: - 当多个请求同时请求同一个未缓存的资源时,只让一个请求去后端获取 - 其他请求等待第一个请求完成后从缓存读取 - 防止缓存击穿导致后端压力骤增 **配置示例**: ```yaml 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 实现**,增加文件输出和访问/错误日志分离。 **实现**: ```go // 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` - 请求耗时 **配置示例**: ```yaml logging: access: path: /var/log/lolly/access.log # 留空则输出到 stdout format: json # json 或 text(Phase 2 ConsoleWriter) error: path: /var/log/lolly/error.log # 留空则输出到 stderr level: info # debug/info/warn/error ``` #### 5.5 状态监控端点 **实现**: ```go // internal/server/status.go // StatusHandler 状态监控处理器 type StatusHandler struct { server *Server } // 返回数据 type Status struct { Connections int Requests int64 BytesSent int64 BytesReceived int64 Uptime time.Duration } ``` **配置示例**: ```yaml monitoring: status: path: /_status # 状态端点路径 allow: [127.0.0.1] # 仅允许本地访问 ``` ### 验证方法 ```bash # 重写测试 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 代理 **实现**: ```go // 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 } ``` **配置示例**: ```yaml 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 优雅升级(热升级) **实现**: ```go // internal/server/upgrade.go // GracefulUpgrade 优雅升级 func GracefulUpgrade(newBinary string) error // 逻辑: // 1. 启动新进程,继承监听 socket // 2. 新进程开始接受新连接 // 3. 旧进程停止接受新连接,完成现有请求后退出 ``` **信号处理**: - `SIGUSR2`:触发升级 - `SIGWINCH`:优雅关闭 worker #### 6.3 性能优化 **优化点**: ##### 6.3.1 连接复用 ```go // http.Transport 连接池配置 transport := &http.Transport{ MaxIdleConns: 100, // 最大空闲连接数 MaxIdleConnsPerHost: 32, // 每主机最大空闲连接 IdleConnTimeout: 90 * time.Second, // 空闲连接超时 MaxConnsPerHost: 0, // 每主机最大连接数(0=无限制) } ``` ##### 6.3.2 缓冲池 ```go // 使用 sync.Pool 实现分级缓冲池 var bufferPool = sync.Pool{ New: func() interface{} { return make([]byte, 32*1024) // 32KB 缓冲区 }, } ``` ##### 6.3.3 零拷贝(sendfile) ```go // 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 池(可选) ```go // 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 对象池 ```go // 使用 sync.Pool 复用对象 var requestPool = sync.Pool{ New: func() interface{} { return new(Request) }, } ``` ##### 6.3.6 代理缓存锁(防击穿) ```go // 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` | 热升级 | ### 验证方法 ```bash # TCP Stream 测试 # 启动 MySQL 后端 mysql -h localhost -P 3306 # 应通过 lolly 代理连接 # 热升级测试 kill -USR2 # 触发升级 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(可选) ``` --- ## 第七阶段:功能完善 ### 目标 补齐项目缺失功能,提升完整性和生产可用性。 ### 背景分析 通过代码审查发现以下缺失功能: | 功能 | 当前状态 | 优先级 | 说明 | |------|----------|--------|------| | WebSocket 代理 | ⚠️ 返回 501 | P0 | 现代Web应用必备 | | 状态监控端点 | ⚠️ 配置已有,处理器缺失 | P1 | 运维必需 | | 代理缓存集成 | ⚠️ 缓存模块未集成 | P1 | 性能优化关键 | | Brotli 压缩 | ⚠️ 降级为 gzip | P2 | 需引入依赖 | | UDP Stream | ⚠️ Accept 返回 EOF | P2 | 需重写处理逻辑 | | OCSP Stapling | ❌ 未实现 | P3 | 安全增强(可选)| ### 任务列表 #### 7.1 WebSocket 代理 (P0) **问题**:`proxy.go:344-349` 当前返回 501 Not Implemented。 **实现方案**: ```go // internal/proxy/websocket.go // WebSocketBridge WebSocket 桥接器。 type WebSocketBridge struct { clientConn net.Conn targetConn net.Conn } // Bridge 双向转发 WebSocket 数据。 func (b *WebSocketBridge) Bridge() error { // 使用 io.Copy 双向转发 // 客户端 → 后端 // 后端 → 客户端 } ``` **实现要点**: - 使用 `ctx.Hijack()` 获取底层 TCP 连接 - 建立到后端的 TCP 连接 - 发送 HTTP 升级请求 - 启动双向 io.Copy 数据转发 - 处理连接关闭和错误 **修改文件**: - `internal/proxy/proxy.go:341-349` - 调用 WebSocket 桥接 - 新增 `internal/proxy/websocket.go` - WebSocket 桥接逻辑 #### 7.2 状态监控端点 (P1) **问题**:`config.go:222-231` 已定义配置,但未实现处理器。 **实现方案**: ```go // internal/server/status.go // StatusHandler 状态监控处理器。 type StatusHandler struct { server *Server allowed []net.IPNet } // Status 状态响应结构。 type Status struct { Version string `json:"version"` Uptime time.Duration `json:"uptime"` Connections int64 `json:"connections"` Requests int64 `json:"requests"` BytesSent int64 `json:"bytes_sent"` BytesReceived int64 `json:"bytes_received"` } // ServeHTTP 返回 JSON 格式状态。 func (h *StatusHandler) ServeHTTP(ctx *fasthttp.RequestCtx) { // 1. 检查 IP 访问权限 // 2. 收集状态数据 // 3. 返回 JSON 响应 } ``` **修改文件**: - 新增 `internal/server/status.go` - 状态处理器 - `internal/server/server.go:54,95` - 注册状态路由 #### 7.3 代理缓存集成 (P1) **问题**:`cache/file_cache.go` 已实现 `ProxyCache`,但未与代理集成。 **实现方案**: ```go // internal/proxy/proxy.go // Proxy 添加缓存字段。 type Proxy struct { // ... 现有字段 cache *cache.ProxyCache // 新增 } // ServeHTTP 集成缓存逻辑。 func (p *Proxy) ServeHTTP(ctx *fasthttp.RequestCtx) { // 1. 生成缓存键 key := p.cacheKey(ctx) // 2. 尝试获取缓存 if entry, ok, stale := p.cache.Get(key); ok { // 返回缓存响应 p.serveFromCache(ctx, entry, stale) return } // 3. 检查缓存锁(防击穿) if waitCh := p.cache.AcquireLock(key); waitCh != nil { <-waitCh // 等待其他请求生成缓存 // 重新获取缓存 } // 4. 请求后端 // 5. 存入缓存 } ``` **修改文件**: - `internal/proxy/proxy.go` - 添加缓存逻辑 - `internal/server/server.go:129-161` - 传递缓存配置 #### 7.4 Brotli 压缩 (P2) **问题**:`compression.go:209-214` 降级为 gzip。 **实现方案**: ```go // internal/middleware/compression/compression.go import "github.com/andybalholm/brotli" // compressBrotli 使用 brotli 压缩数据。 func (m *CompressionMiddleware) compressBrotli(data []byte) []byte { var buf bytes.Buffer w := brotli.NewWriterLevel(&buf, m.level) w.Write(data) w.Close() return buf.Bytes() } ``` **实现要点**: - 添加依赖:`go get github.com/andybalholm/brotli` - 添加 brotli 缓冲池 - 更新 `compressBrotli` 方法 **修改文件**: - `internal/middleware/compression/compression.go` #### 7.5 UDP Stream 完善 (P2) **问题**:`stream.go:388-391` `udpListener.Accept()` 返回 EOF。 **原因分析**:UDP 是数据报协议,不能像 TCP 那样 Accept 连接。 **实现方案**: ```go // internal/stream/stream.go // udpServer UDP 服务器。 type udpServer struct { conn *net.UDPConn sessions map[string]*udpSession // 客户端地址 → 会话 mu sync.RWMutex } // udpSession UDP 会话。 type udpSession struct { clientAddr *net.UDPAddr targetConn net.Conn lastActive time.Time } // serveUDP 处理 UDP 数据报。 func (s *udpServer) serveUDP() { buf := make([]byte, 65535) for { n, clientAddr, err := s.conn.ReadFromUDP(buf) if err != nil { continue } // 查找或创建会话 session := s.getOrCreateSession(clientAddr) // 转发数据到后端 session.targetConn.Write(buf[:n]) } } ``` **修改文件**: - `internal/stream/stream.go:199-217,383-401` - 重写 UDP 处理 #### 7.6 OCSP Stapling (P3 - 可选) **问题**:`ssl.go` 未实现 OCSP Stapling。 **实现方案**: - 在 TLS 配置中添加 `GetConfigForClient` 回调 - 定期查询 OCSP 服务器并缓存响应 - 在 TLS 握手中附加 OCSP 响应 **注意**:此功能实现复杂,可作为后续迭代项。 ### 验证方法 ```bash # WebSocket 测试 wscat -c ws://localhost:8080/ws # 状态监控测试 curl http://localhost:8080/_status # 代理缓存测试(检查响应头) curl -I http://localhost:8080/api/data # 第二次请求应返回 X-Cache-Status: HIT # Brotli 压缩测试 curl -H "Accept-Encoding: br" -I http://localhost:8080/index.html # 检查 Content-Encoding: br # UDP Stream 测试 dig @localhost example.com # 通过 lolly 代理 DNS ``` ### 文件依赖关系图 ``` Phase 7: internal/proxy/proxy.go → internal/proxy/websocket.go(新增) internal/proxy/proxy.go → internal/cache/file_cache.go internal/server/server.go → internal/server/status.go(新增) internal/middleware/compression/compression.go → github.com/andybalholm/brotli internal/stream/stream.go → 重写 UDP 处理 ``` --- **Phase 2 技术选型变更**: - HTTP 库:使用 [fasthttp](https://github.com/valyala/fasthttp) 替代 `net/http`(性能提升 6 倍) - 日志库:使用 [zerolog](https://github.com/rs/zerolog)(零分配,~40ns/op) --- ## 参考文档 详细功能参考 `docs/` 目录: - HTTP 核心:`docs/03-nginx-http-core.md` - 代理负载均衡:`docs/04-nginx-proxy-loadbalancing.md` - SSL/HTTPS:`docs/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`(必须遵循) --- ## 第八阶段:问题修复与功能完善 ### 目标 修复深度分析发现的三个遗留问题,提升项目生产可用性。 ### 背景分析 通过代码审查发现以下问题: | 问题 | 当前状态 | 优先级 | 说明 | |------|----------|--------|------| | WebSocket 代理未集成 | ⚠️ 返回 501 | P0 | 已实现但未调用 | | UDP Stream 冗余代码 | ⚠️ Accept 返回 EOF | P1 | 设计问题,需删除 | | 热升级监听器继承 | ⚠️ GetListeners 返回空 | P1 | 监听器未保存 | ### 任务列表 #### 8.1 WebSocket 代理集成 (P0) **问题**:`proxy.go:370-375` 的 `handleWebSocket` 返回 501 Not Implemented,但 `websocket.go` 中已有完整的 `ProxyWebSocket` 实现。 **修改文件**:`internal/proxy/proxy.go:370-375` **修改内容**: ```go // 修改前 func (p *Proxy) handleWebSocket(ctx *fasthttp.RequestCtx, target *loadbalance.Target, client *fasthttp.HostClient) { ctx.Error("WebSocket proxying not implemented", fasthttp.StatusNotImplemented) } // 修改后 func (p *Proxy) handleWebSocket(ctx *fasthttp.RequestCtx, target *loadbalance.Target, client *fasthttp.HostClient) { timeout := p.config.Timeout.Connect if timeout == 0 { timeout = 30 * time.Second } if err := ProxyWebSocket(ctx, target, timeout); err != nil { logging.Error().Msgf("WebSocket proxy error: %v", err) } } ``` #### 8.2 删除冗余 UDP 监听器 (P1) **问题**:`stream.go:435-453` 的 `udpListener` 类型实现 `net.Listener` 接口,但 UDP 是无连接协议,`Accept()` 始终返回 `io.EOF`。实际 UDP 处理由 `udpServer` 完成。 **修改文件**: - `internal/stream/stream.go:435-453` - 删除 `udpListener` 类型 - `internal/stream/stream_test.go` - 删除相关测试 **删除代码**: ```go // 删除以下代码(第 435-453 行) type udpListener struct { conn *net.UDPConn } func (u *udpListener) Accept() (net.Conn, error) { return nil, io.EOF } func (u *udpListener) Close() error { return u.conn.Close() } func (u *udpListener) Addr() net.Addr { return u.conn.LocalAddr() } ``` #### 8.3 热升级监听器继承 (P1) **问题**: 1. `Server.listeners` 字段从未被赋值 2. `fasthttp.ListenAndServe()` 内部创建监听器但未暴露 3. 子进程未使用继承的监听器 **修改文件**: - `internal/server/server.go` - 改用手动监听器管理 - `internal/app/app.go` - 支持继承监听器启动 **核心修改**: ```go // server.go - 使用 net.Listen + fasthttp.Serve 替代 ListenAndServe // 创建监听器 ln, err := net.Listen("tcp", s.config.Server.Listen) if err != nil { return fmt.Errorf("failed to listen: %w", err) } s.listeners = []net.Listener{ln} // 保存监听器 // 使用 Serve 替代 ListenAndServe if s.tlsConfig != nil { return s.fastServer.ServeTLS(ln, "", "") } return s.fastServer.Serve(ln) ``` ```go // app.go - 子进程继承监听器 if os.Getenv("GRACEFUL_UPGRADE") == "1" { fmt.Println("检测到热升级模式,继承父进程监听器") listeners, err := a.upgradeMgr.GetInheritedListeners() if err == nil && len(listeners) > 0 { a.srv.SetListeners(listeners) } } ``` ### 验证方法 ```bash # 1. WebSocket 测试 wscat -c ws://localhost:8080/ws # 2. UDP Stream 测试 go test ./internal/stream/... -v # 3. 热升级测试 ./lolly -c config.yaml & kill -USR2 # 验证新进程接管,旧进程优雅退出 # 4. 完整测试 go test ./... -race go build ./... ``` ### 文件依赖关系图 ``` Phase 8: internal/proxy/proxy.go → internal/proxy/websocket.go(调用) internal/stream/stream.go → 删除 udpListener internal/server/server.go → net.Listen + fasthttp.Serve internal/app/app.go → GetInheritedListeners ``` --- ## 总体进度追踪(更新) | 阶段 | 状态 | 主要功能 | | ------- | ------ | ------------------------- | | Phase 1 | ✅ 完成 | 项目骨架、配置系统 | | Phase 2 | ✅ 完成 | HTTP 核心、静态文件、路由 | | Phase 3 | ✅ 完成 | 反向代理、负载均衡 | | Phase 4 | ✅ 完成 | SSL/TLS、安全控制 | | Phase 5 | ✅ 完成 | 重写、压缩、缓存、日志 | | Phase 6 | ✅ 完成 | Stream、性能优化、热升级 | | Phase 7 | ✅ 完成 | 功能完善 | | Phase 8 | ✅ 完成 | 问题修复(WebSocket集成、UDP清理、热升级修复)| **Phase 8 完成日期**:2026-04-03 **修复内容**: 1. WebSocket 代理集成 - `handleWebSocket` 现调用 `ProxyWebSocket` 2. UDP Stream 冗余代码 - 删除 `udpListener` 类型及相关测试 3. 热升级监听器继承 - 改用 `net.Listen` + `Serve` 模式,支持监听器继承